【问题标题】:static member of class template error类模板的静态成员错误
【发布时间】:2014-04-24 08:05:27
【问题描述】:

我对这段代码 sn-p 有问题:

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 接受。哪个编译器是正确的?我以后应该避免使用此类代码吗?

【问题讨论】:

  • +1 好问题。我最初的反应是 g++ 的接受一定是错误的,但转念一想,它一定是对的。嗯。
  • Clang 至少在 wordings 中是错误的:previous definition is here。嗯,那是不是一个定义;这是一个声明。
  • +1。聪明的问题,它试图避免再次输入类型。以后如果想改变类型,只需要改变声明即可;它也会自动反映在定义中。
  • VC++ 也没有报错。

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


【解决方案1】:

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 [basic]、§7 [dcl.dcl]、§8 [dcl.decl] 某处的实际标准语, §14 [temp],或者其他地方。


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

如果表达式e涉及模板参数,decltype(e) 表示唯一的依赖类型。两个这样的 decltype-specifiers 指 只有当它们的表达式是等价的 (14.5.6.1) 时才属于相同的类型。 [注意: 然而,它可能是别名,例如,由 typedef-name. - end 注意 ]

我很确定定义与声明相匹配需要等价,而不仅仅是别名。 §14.5.6.1 深入研究了这个领域,除了专门讨论函数签名。

【讨论】:

    【解决方案2】:

    我认为 Clang 可能拒绝这个是正确的。 14.2p2 说decltype(e)

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

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

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

    ...

    一般来说,如果您可以仅使用模板中的信息匹配声明,则声明是有效的。

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


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

    对于可以生成有效专业化的模板,不应发出诊断。

    可以将其理解为类型等价检查只是延迟到实例化之后。但是,这与我认为的 DR 中的讨论轨迹相矛盾,因为他们说“如果您可以仅使用模板中的信息来匹配声明,那么声明是有效的”(我假设该声明的作者意味着详细说明声明有效的情况)。

    【讨论】:

      【解决方案3】:

      对我来说,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)

      编辑: 我发现这个话题直到现在在标准中只是“没有解决”,而clang没有实现它: 看一眼: http://clang.llvm.org/cxx_dr_status.html(第 205 点)

      我希望我没有误解该页面。随时纠正我的解释。

      【讨论】:

      • 很好的旁证,让我们拭目以待语言律师会怎么说。是否愿意发布 clang 错误?
      • 这个话题已经在列表中了,为什么还要有另一个错误报告。特别是对于这一点:clang 没有错误,这似乎是标准中的一个悬而未决的问题,因此实施者没有机会以正确的方式完成工作。
      • clang 声称的目标是接受gcc 接受的所有内容。因此,此编译失败可能被视为错误。另请注意,即使您也不知道我的问题是否完全符合未解决的clang 问题。
      • 随意吧。我不必决定应该写哪些错误报告 :-) 对我来说,clang 团队知道这个话题仍然是开放的。
      • 我看不出 DR 或其他问题与这个问题有何关系。无论静态成员本身是模板还是实例化时,定义声明都与声明匹配,类型没有区别。
      猜你喜欢
      • 2017-10-20
      • 1970-01-01
      • 2012-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多