类模板的静态成员错误

static member of class template error

本文关键字:静态成员 错误      更新时间:2023-10-16

我对以下代码段有问题:

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接受。哪个编译器是对的?我以后应该避免这样的代码吗?

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.dll]、§14[临时]或其他地方找到实际标准。


我仍然找不到什么规则将对象定义与前面的声明相匹配,但§14.4/2规定decltype(…)不能等同于int(我认为是在声明意义上)。

如果表达式e涉及模板参数decltype(e)表示唯一的依赖类型。两个这样的decltype说明符引用仅当它们的表达式相等时(14.5.6.1),才将其转换为相同类型。[注意:但是,它可能会被别名,例如通过typedef名称。--end注意]

我确信等价性,而不仅仅是别名,对于定义匹配声明是必要的。§14.5.6.1深入探讨了这一领域,只是专门讨论了函数签名。

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

如果表达式e包含一个模板参数,decltype(e)表示一个唯一的依赖类型。

在DR#2中,讨论跟踪显示

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

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

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


为什么我说可能?由于14.6p8

对于可以生成有效专业化的模板,不得发布任何诊断。

可以将其理解为,类型等价性检查只是延迟到实例化之后。然而,我认为这将与DR中的讨论轨迹相矛盾,因为他们说"如果你可以只使用模板中的信息来匹配声明,那么声明是有效的"(我认为本声明的作者是为了详尽地说明声明有效的情况)。

对我来说,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++静态成员初始化(内部模板乐趣)

编辑:直到现在,我发现这个主题在标准中根本"没有得到解决",clang也没有实现它:看看:http://clang.llvm.org/cxx_dr_status.html(点205)

我希望我没有误解这一页。请随意更正我的解释。