当前位置: 首页 > 知识库问答 >
问题:

这真的是依赖模板名称吗?

岑和风
2023-03-14

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

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;
}

在魔杖盒上看到

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>();
      |       

按照叮当的建议改变冒犯的路线

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

修复Clang和GCC的编译。VS2017也接受此版本。

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

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

或从 foo 中删除参数:

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

错误消失了。

这到底是怎么回事?

    < Li > Clang和GCC right是否拒绝原始版本? < Li > Clang和GCC right是否接受更改的版本(重新排序、更改的< code>foo)?如果有,为什么?有什么区别?

共有1个答案

於功
2023-03-14

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

有问题的代码确实需要模板来消除bar是模板名称的歧义。gcc和clang在诊断这一点时是正确的。可能存在foo的重载,根据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>();

重载的可能性(而不是实际存在)使名称依赖于。请参考[临时部门](强调我的):

在模板内部,一些结构的语义学可能因实例而异。这种结构取决于模板参数。

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

 类似资料:
  • 考虑以下示例: GCC和Clang都未能在C 17标准下编译此代码(使用Werror),因为(根据我的理解)在C 17 ADL中,当显式模板参数

  • 当我尝试编译这段代码时 对于g 4.8.2,我得到以下错误消息 (与3.4版本的叮当声几乎相同)。 首先,我认为代码是正确的,应该进行编译,因为foobar是模板声明中的一个依赖名称,应该仅在模板实例化的第二阶段进行查找。在最后一行中完成此操作时,已声明“foobar(int)”。顺便说一句,当我取消注释最上面的行时,代码会编译,但这两个声明都在实例化之前,所以这应该无关紧要。 其次,我觉得错误信

  • 问题内容: 在(和)包中,具有以下签名: 到底是干什么用的?我已经扫描了文档(还有一些资料),但无济于事。我只是用一个空字符串实例化所有模板,这似乎没有什么不同。我为什么要打扰一个名字? 即使是命名模板,两者似乎是等效的: https://play.golang.org/p/wKzCHdLf2S 问题答案: 毫无疑问,模板的名称就是为模板 命名 。 到底有什么好处呢?只要您不想 引用 该模板,就没

  • 这是关于cppreference的描述。com说 模板中使用的依赖名称的查找将推迟到模板参数已知时,此时[…]ADL检查从模板定义上下文或模板实例化上下文可见的具有外部链接的函数声明。 与此相反,以下代码段可以使用三个编译器(MSVC、clang、gcc)很好地编译: Foo是CallFoo中的从属名称:它取决于模板参数T。但是,尽管违反了上述两条规则,编译器还是找到了函数Foo。 从的定义或实例

  • 我在一个ARM模板中有两个相互依赖的Azure资源:一个密钥库和一个service fabric集群。 是否有一种方法可以引用service fabric集群的对象ID来提供给密钥库的访问策略,是否有一种方法可以在不硬编码任何值的情况下生成密钥库中的机密?理想情况下,我们只知道秘密名称,并且只将该秘密名称提供给ARM模板中的服务fabric集群。

  • 如何在某个模板类的基础上,通过写死(绑定)部分模板参数类型,来定制出一个新的模板类? 尝尝我这种写法如何: template<class T> // 采用了自定义内存分配器的std::vector using Vec = std::vector<T,My_alloc<T>>; // 使用My_alloc为元素分配存储空间 Vec<int> fib = { 1, 2, 3, 5, 8, 13 };