25

我有一个我static_assert的模板结构的移动构造函数。static_assert即使复制省略是可能的,编译器是否需要考虑这一点?

这是精简的场景:

#include <type_traits>

template<typename T>
struct X
{
  X(X&&) { static_assert(std::is_same<void, T>::value, "Intentional Failure"); }
};

auto impl() -> X<int>;    
auto test() -> decltype(impl())
{
  return impl();
}

int main()
{
  test();
}

GCC 和 Clang 同意评估static_assert和编译失败。
另一方面,MSCV 和 ICC 可以很好地编译代码。

有趣的是,当我删除move构造函数的定义并像这样声明它时:

template<typename T>
struct X
{
  X(X&&);
};

GCC 和 Clang 现在也编译代码。因此,所有编译器似乎都同意移动构造函数的定义与复制省略无关。

问题
如果static_assert复制/移动构造函数中有 a ,标准是否要求评估它,即使复制/移动省略是可能的?

4

4 回答 4

4

以下应该会有所帮助。

您不必使用类型推导来说明问题。即使是更简单的例子也有同样的问题:

#include <type_traits>

template <typename T>
struct X
{
  X() {}
  X(X&&) { static_assert(std::is_same<void, T>::value, "failed"); }
};

int main()
{
  X<int> x = X<int>();
}

Clang 和 GCC 不会编译它。MSVC 编译并执行良好。

这表明问题与使用 odr 以及何时实例化成员函数的定义有关。

14.7.1 [temp.inst] 第 2 段说“[...] 当在需要成员定义存在的上下文中引用特化时,成员的特化被隐式实例化”

3.2 [basic.def.odr] 第 3 段说(在注释中)“[...] 选择用于复制或移动类类型对象的构造函数是 odr 使用的,即使调用实际上被实现省略了”

3.2 [basic.def.odr] 第 4 段说“每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断。”

尔格:应该实例化特化,并且断言已经触发。

于 2016-04-11T12:43:08.490 回答
2

我相信答案是否定的。我的逻辑是这样的:

  1. 复制省略需要声明复制/移动构造函数,但不需要定义。
  2. 除非需要它们的定义,否则不会实例化模板的成员函数定义。
  3. 如果一个定义没有被实例化,它就不能被测试为格式错误。

参考:

14.7.1.1 …类模板特化的隐式实例化会导致声明的隐式实例化,但不会导致类成员函数的定义、默认参数或异常规范的隐式实例化…</p>

14.7.1.2除非类模板的成员……已被显式实例化或显式特化,否则当在需要成员定义存在的上下文中引用特化时,成员的特化将被隐式实例化……</p>

于 2016-04-05T11:48:54.180 回答
1

不调用移动构造函数。static_assert在 的实例化时进行评估X<int>::X(X&&)。最有可能的是,一些编译器在使用时评估模板方法(当您使用移动构造函数时,但您不使用它),而其他编译器 - 在实例化类模板时(当您第一次使用时X<int>)。

于 2016-04-05T07:05:19.103 回答
-1

我认为答案是:是的。

首先,static_assert 强制构造函数从“定义”变为声明。

关于下面的 12.8 部分,我不确定 static_assert 的模板性质......

(我为格式道歉......)

c © ISO/IEC N3242=11-0012 7 声明 [dcl.dcl]

2. 声明是一个定义,除非它声明一个函数而不指定函数体 (8.4),它包含 extern 说明符 (7.1.1) 或链接规范 25(7.5),既不是初始化程序也不是函数体,它在类定义(9.4)中声明一个静态数据成员,它是一个类名声明(9.1),它是一个不透明枚举声明(7.2),或者它是一个typedef声明(7.1.3),一个使用-declaration(7.3.3)、static_assert-declaration(Clause 7)、attribute-declaration (Clause 7)、empty-declaration (Clause 7) 或 using-directive (7.3.4)

12.8 复制和移动类对象[class.copy]

7 永远不会实例化成员函数模板来执行将类对象复制到其类类型的对象。[例子:

struct S {
template<typename T> S(T);
template<typename T> S(T&&);
S();
};

S f();
const S g;

void h() {
S a( f() );// does not instantiate member template;
// uses the implicitly generated move constructor


S b(g);// does not instantiate the member template;
// uses the implicitly generated copy constructor
}

—结束示例]

32 当满足某些条件时,允许实现省略类对象的复制/移动构造,即使对象的复制/移动构造函数和/或析构函数有副作用。在这种情况下,实现将省略的复制/移动操作的源和目标简单地视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象本应被删除的较晚时间。没有优化就被破坏了。123 - 这种复制/移动操作的省略,称为复制省略,在以下情况下是允许的(可以组合起来消除多个副本): - 在具有类返回类型的函数的返回语句中,

于 2016-04-02T15:02:53.297 回答