当前位置:  开发笔记 > 编程语言 > 正文

为什么别名模板会给出冲突的声明?

如何解决《为什么别名模板会给出冲突的声明?》经验,为你挑选了1个好方法。

从Clang到g ++的一些C++ 11代码的端口

template
using value_t = typename T::value_type;

template
struct S
{
    using value_type = int;
    static value_type const C = 0;
};

template 
value_t> // gcc error, typename S::value_type does work
const S::C;

int main() 
{    
    static_assert(S::C == 0, "");
}

给出了Clang(版本3.1到SVN主干)与任何g ++版本的不同行为.对于后者我得到的错误,这样的

prog.cc:13:13: error: conflicting declaration 'value_t > S<  >::C'
 const S::C;
             ^
prog.cc:8:29: note: previous declaration as 'const value_type S<  >::C'
     static value_type const C = 0;
                             ^
prog.cc:13:13: error: declaration of 'const value_type S<  >::C' outside of class is not definition [-fpermissive] const S::C;

如果不value_t>使用模板别名我使用完整,typename S::value_type那么g ++也可以.

问题:模板别名是不是应该与其底层表达式完全互换?这是一个g ++错误吗?

更新:Visual C++还接受类外定义中的别名模板.



1> Mário Ferold..:

问题依赖于SFINAE.如果您重写您的成员函数value_t>,就像外部声明一样,那么GCC将很乐意编译它:

template
struct S
{
    using value_type = int;
    static const value_t> C = 0;
};

template 
const value_t> S::C;

因为表达式现在在功能上是等效的.替换失败之类的事情在别名模板上发挥作用,但正如您所见,成员函数value_type const C没有与之相同的"原型" value_t> const S::C.第一个不必执行SFINAE,而第二个需要它.很明显,两个声明都有不同的功能,因此GCC发脾气.

有趣的是,Clang编译它没有异常的迹象.我认为恰恰相反,与GCC相比,Clang分析的顺序是相反的.一旦alias-template表达式被解析并且很好(即它是格式良好的),clang然后比较两个声明并检查它们是否等效(在这种情况下它们是,两个表达式都解析为value_type).

现在,从标准的眼睛看哪一个是正确的?将alias-template的SFNIAE视为其声明功能的一部分仍然是一个尚未解决的问题.引用[temp.alias]/2:

当template-id引用别名模板的特化时,它等同于通过替换别名模板的type-id中的template-parameters的template-arguments获得的关联类型.

换句话说,这两个是等价的:

template
struct Alloc { /* ... */ };

template
using Vec = vector>;

Vec v;
vector> u;

Vec并且vector>是等价的类型,因为在执行替换之后,两种类型最终都是vector>.注意"替换后"意味着只有在用模板参数替换所有模板参数后才检查等价.也就是说,当Tin vector>替换为intfrom 时,比较开始Vec.也许这就是Clang正在做的事情value_t>?但是,[temp.alias]/3中有以下引用:

但是,如果template-id是依赖的,则后续模板参数替换仍适用于template-id.[例:

template using void_t = void;
template void_t f();
f(); // error, int does not have a nested type foo

 - 结束例子]

这里的问题:表达得到很好的形成,所以编译器需要检查是否替代是罚款.当存在依赖性以便执行模板参数替换(例如typename T::foo)时,整个表达式的功能改变,并且"等价"的定义不同.例如,以下代码将无法编译(GCC和Clang):

struct X
{
    template 
    auto foo(T) -> std::enable_if_t;
};

template 
auto X::foo(T) -> void
{}

因为外部foo的原型在功能上与内部原型不同.auto X::foo(T) -> std::enable_if_t相反,这样可以使代码编译正常.这是因为返回类型foo是一个依赖于结果的表达式sizeof(T) == 4,所以在模板替换之后,它的原型可能与它的每个实例不同.然而,auto X::foo(T) -> void返回类型永远不会有所不同,这与内部的声明相冲突X.这与您的代码发生的问题完全相同.因此GCC在这种情况下似乎是正确的.

推荐阅读
赛亚兔备_393
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有