Clang 和 GCC 与 MSVC 和 ICC:如果 copy/move 省略也适用,copy/move 构造函数中的 static_assert 是否需要工作?

Clang and GCC vs MSVC and ICC: Is a static_assert in the copy/move constructor required to work, if copy/move elision could apply too?

我在我的模板结构的移动构造函数中有一个 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 现在也编译代码。因此,所有编译器似乎都同意移动构造函数的定义与复制省略无关。

问题:
如果 copy/move 构造函数中有一个 static_assert,即使 copy/move 省略是可能的,标准是否要求对它求值?

我认为答案是:是的。

首先,static_assert 强制构造函数从 "definition" 声明。

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

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

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

2。 声明是一个定义,除非它声明一个函数而不指定函数的主体(8.4),它包含 extern 说明符(7.1.1)或链接规范 25(7.5)并且既不是初始值设定项也不是函数主体,它声明class 定义 (9.4) 中的静态数据成员,它是 class 名称声明 (9.1),它是不透明枚举声明 (7.2),或者它是 typedef 声明 (7.1 .3), using-declaration(7.3.3), static_assert-declaration(Clause 7), attribute-declaration (Clause 7), empty-declaration (Clause 7), or using-directive (7.3.4)

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

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

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 当满足某些条件时,允许实现省略 class 对象的 copy/move 构造,即使 copy/move 构造函数 and/or 析构函数该对象有副作用。在这种情况下,实现将省略的 copy/move 操作的源和目标视为引用同一对象的两种不同方式,并且该对象的销毁发生在两个对象将要删除的较晚的时间。没有优化就被破坏了。 123 - 这种 copy/move 操作的省略,称为复制省略,在以下情况下是允许的(可以组合起来消除多个副本): — 在具有 class return 类型的函数中的 return 语句中,当表达式是非易失性自动对象的名称时(函数或 catch 子句参数除外)与函数 return 类型相同的 cv-unqualified 类型,可以通过将自动对象直接构造成函数的 return 值

来省略 copy/move 操作

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

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

  1. 复制省略需要声明 copy/move 个构造函数,但不需要定义。
  2. 模板的成员函数定义不会被实例化,除非它们的定义是必需的。
  3. 如果定义未被实例化,则无法对其进行格式错误测试。

参考文献:

14.7.1.1 ...class 模板特化的隐式实例化导致声明的隐式实例化,但不是定义、默认参数或异常的隐式实例化- class 成员函数的规范…

14.7.1.2 除非 class 模板的成员…已被显式实例化或显式特化,当特化是在需要成员定义存在的上下文中引用...

以下应该有所帮助。

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

#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-use 和实例化成员函数的定义有关。

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

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

3.2 [basic.def.odr] 第 4 段 说 "Every program shall contain exactly one definition of every non-inline function or variable that is odr-used in that program; no diagnostic required."

因此:专业化应该被实例化,断言已经触发。