类模板的静态成员错误

时间:2014-04-24 08:05:27

标签: c++ c++11 language-lawyer

我对此代码段有疑问:

template <typename T>
struct S
{
  static int a;
};

template <typename T>
decltype(S<T>::a) S<T>::a;

clang-3.4说:

s.cpp:8:25: error: redefinition of 'a' with a different type: 'decltype(S<T>::a)' vs 'int'
decltype(S<T>::a) S<T>::a;
                        ^
s.cpp:4:14: note: previous definition is here
  static int a;
             ^
1 error generated.

但是gcc-4.8.2接受了。哪个编译器是对的?我将来应该避免使用这些代码吗?

3 个答案:

答案 0 :(得分:3)

Clang要求定义在模板定义时匹配声明,而GCC和其他人推迟匹配直到实例化时间(从来没有发生过你的例子)。

Clang接受这个:

#include <type_traits>

template <typename T>
struct S
{
  static int a;
};

template <typename T>
typename std::enable_if< true, int >::type S<T>::a; // Resolves before instantiation

但拒绝这一小改变:

template <typename T>
typename std::enable_if< std::is_same< T, T >::value, int >::type S<T>::a;

我不记得在发生对象声明匹配时标准规定的位置,但我怀疑Clang是否有权拒绝代码。如果我没记错的话,标准的意图是每个声明只匹配一个定义,并且可以在实例化时间之前确定该映射。

根据GCC显然适用的更宽松规则,您可以有两个成员声明和两个定义,但每个定义可以根据模板参数完成任一个声明。

GCC和MSVC接受的代码格式不正确,无需诊断......等待在[§3[基本],§7[dcl.dcl],§8[dcl.decl]中找到埋藏在实际标准中的实际标准, §14[temp],或者其他地方。


我仍然无法找到哪些规则将对象定义与前面的声明匹配,但是§14.4/ 2规定decltype(…)不能与int等效(我假设在声明意义上)。

  

如果表达式 e 涉及模板参数,decltype(e)   表示唯一的依赖类型。两个这样的 decltype-specifiers 是指   只有当它们的表达式相同时才能使用相同的类型(14.5.6.1)。   [注意:但是,它可能是别名,例如,通过 typedef-name。 - 结束   注意]

我很确定定义匹配声明需要等效,而不仅仅是别名。 §14.5.6.1深入研究这个领域,除非它专门讨论功能签名。

答案 1 :(得分:3)

我认为Clang 可能正确拒绝这一点。 14.2p2说decltype(e)

  

如果表达式e涉及模板参数,则decltype(e)表示唯一的依赖类型。

DR #2中,讨论痕迹显示

  

我的观点(我认为与最近在反射器上发布的几个相匹配)是,类外定义必须与模板中的声明相匹配。

     

...

     

通常,如果只使用模板中的信息匹配声明,则声明有效。

我认为如果其中一个使用typedef(如DR中所示),它仍然匹配,因为S<T>::type是当前实例化的成员,并且可以直接查找别名类型。但是,如上所述,decltype(e)将始终表示唯一类型(在模板分析时间内),除了指定等效表达式的另一个decltype(e)之外。


为什么我说可能?因为14.6p8

  

不能为可以生成有效特化的模板发出诊断。

可以读到这一点,因为类型等价检查只是延迟到实例化之后。然而,这与我认为的DR中的讨论痕迹相矛盾,因为他们说如果你能够仅使用来自模板的信息来匹配声明,那么声明是有效的&#34; (并且我假设本声明的作者意图详尽说明声明有效时的情况。)

答案 2 :(得分:2)

对我来说,clang在这里被打破了。

所有与decltype的组合都将失败。没有decltype就行了。

template <typename T>
struct S
{   
      static int a;

      using type = decltype( a );
      typedef decltype( a ) type2;
};  

template <typename T>
1) decltype(S<T>::a) S<T>::a;
2) int S<T>::a;
3) typename S<T>::type S<T>::a;
4) typename S<T>::type2 S<T>::a;

1 gcc工作,clang失败

2 gcc + clang作品

3 gcc工作,clang失败

4 gcc的作品,clang失败

我做了一些尝试解决问题,但没有取得任何成功。

关于这类问题还有更多的讨论: C++ Static member initalization (template fun inside)

编辑: 我发现这个主题只是简单地解决了#34;在标准到现在为止,clang没有实现它: 看一眼: http://clang.llvm.org/cxx_dr_status.html(第205点)

我希望我没有误解这个页面。随意纠正我的解释。