这真的是一个从属模板名称吗?

Is this really a dependent template name?

我有一些代码无法编译,我将其降低到以下最低版本:

class Builder
{
    public:
        Builder()
        {}

        auto foo(int) -> Builder &
        {
            return *this;
        }

        template<typename T>
        auto bar() -> Builder &
        {
            return *this;
        }
};

template<typename T>
Builder get_builder()
{
    return Builder().foo(T()).bar<T>();
}

int main()
{
    auto builder = get_builder<int>();
    (void) builder;
}

See on Wandbox

Clang (9.0.0) 拒绝此:

prog.cc:22:31: error: use 'template' keyword to treat 'bar' as a dependent template name
    return Builder().foo(T()).bar<T>();
                              ^
                              template 

Clang 说 bar 是依赖模板名称是否正确? VS 2017 看不出问题。 GCC (9.2.0) 也拒绝该代码,但错误消息更加模糊:

prog.cc: In function 'Builder get_builder()':
prog.cc:22:36: error: expected primary-expression before '>' token
   22 |     return Builder().foo(T()).bar<T>();
      |                                    ^
prog.cc:22:38: error: expected primary-expression before ')' token
   22 |     return Builder().foo(T()).bar<T>();
      |       

按照 Clang 的建议更改违规行

return Builder().foo(T()).template bar<T>();

修复了 Clang 和 GCC 的编译。 VS2017也接受这个版本

看起来解决方案很简单,但如果我重新排序函数调用:

return Builder().bar<T>().foo(T());

或从 foo 中删除参数:

return Builder().foo().bar<T>();

错误消失了。

这是怎么回事?

  1. Clang 和 GCC 是否拒绝原始版本?
  2. Clang 和 GCC 是否接受更改后的版本(重新排序、更改 foo)? 如果是这样,为什么?有什么区别?

根据我的经验,MSVC(错误地)允许需要 template 关键字的代码(即使定义了 /permissive-)。

有问题的代码 需要 template 来消除 bar 是模板名称的歧义。 gcc 和 clang 对此的诊断是正确的。 foo 的重载可能存在 returns 取决于 T 的不同类型。因此,对 bar 的调用取决于模板参数。

考虑以下几点:

struct FooBar
{
   template<class T>
   auto bar() -> T
   {
       return 42;
   }
};

class Builder
{
    public:
        Builder()
        {}

        auto foo(int) -> Builder &
        {
            return *this;
        }

        auto foo(short) -> FooBar
        {
            return {};
        }

        template<typename T>
        auto bar() -> Builder &
        {
            return *this;
        }
};

调用:

auto builder = get_builder<short>();

重载的可能性(不是实际存在)使名称依赖。参考[temp.dep]强调我的):

Inside a template, some constructs have semantics which may differ from one instantiation to another. Such a construct depends on the template parameters.

编译器没有义务检查模板的所有实例化以查看重载集中是否只有一个可能的函数。