【问题标题】:When are template non-static data member initialisers instantiated?模板非静态数据成员初始化器何时实例化?
【发布时间】:2014-02-15 21:40:01
【问题描述】:

这是一个简短的独立测试用例来解释我的问题。 GCC 接受此代码,但 clang 和 Intel 拒绝它:

template <typename T>
struct false_t {
  static const bool value = false;
};
template <typename T>
int f() {
  static_assert(false_t<T>::value, "");
  return 0;
}
template <typename T>
struct S {
  int m = f<T>();
};
int s = sizeof(S<int>);

或者,根据 pmr 的评论,这是一个更简单的示例,它也被 gcc 接受并被 clang 拒绝:

struct S;
template <typename T> struct X { int x = T(); };
int s = sizeof(X<S>);

sizeof(S&lt;int&gt;)(或sizeof(X&lt;S&gt;))应该实例化它需要的类的位,但编译器不同意这些位。由于非静态数据成员初始化器只能由构造函数使用,因此 GCC 将实例化作为实例化类的构造函数的一部分来执行。 clang 和 Intel 更早地这样做了。

我无法理解标准在 [temp.inst]p1 中所说的内容:

类模板特化的隐式实例化导致隐式 类成员函数、成员类、范围成员枚举、静态数据成员和成员模板的声明的实例化,但不是定义或默认参数的实例化;它会导致无范围成员枚举和成员匿名联合的定义的隐式实例化。

因为我根本看不到非静态数据成员(有或没有初始化器)的声明在哪里被实例化。

关于我在哪里遇到这个问题的更多细节:我正在尝试创建一个模板帮助程序类(永远不会在运行时创建),其中包含一个使用 std::declval&lt;T&gt;() 初始化的成员(我应该补充一下,我现在意识到会无论如何都没有多大用处),并且 libstdc++ 的std::declval 实现包含一个静态断言,就像我的示例中的断言一样。通过避免std::declval,我可以毫不费力地解决这个问题,但我想知道标准要求什么。

【问题讨论】:

  • 也许你应该使用sizeof(S&lt;int&gt;) 而不是sizeof(S&lt;int&gt;::m)。似乎只是掩盖了意图,结果是一样的。
  • @pmr 我在选择其中一个时遇到了麻烦,并且不知何故在代码中使用了后者,但在我对代码的描述中使用了前者。代码现已编辑为使用 sizeof(S&lt;int&gt;),谢谢。
  • 我遇到了这个问题的简单重现:struct S; template&lt;typename T&gt; struct X { int x = T().i; }; unsigned n = sizeof(X&lt;S&gt;);
  • @pmr 不错,这要简单得多!事实上,你甚至不需要那里的.i

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


【解决方案1】:

在试图弄清楚如何提出这个问题时,我偶然发现了答案,但我认为无论如何发布它可能会有用。

这是 C++ 标准的未决问题之一,确切地说是issue 1396。目的是初始化器只在需要时被实例化:

非静态数据成员初始化器获得与成员函数和默认参数相同的后期解析,但它们是否也像它们一样根据需要实例化?什么时候检查它们的有效性?

2012 年 10 月会议记录:

CWG 同意非静态数据成员初始化器应该像默认参数一样处理。

但这种方法仍有很多问题正在解决中。在解决它们之前,不同的编译器在不同的时间执行实例化是很自然的,并且应该重写需要特定行为的代码以避免这种要求。就我而言,这意味着不使用std::declval

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-02
    相关资源
    最近更新 更多