Clang和GCC与MSVC和ICC的比较:如果复制/移动省略也适用,那么在复制/移动构造函数中是否需要静态断言

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?

本文关键字:复制 移动 静态 断言 是否 构造函数 ICC MSVC GCC 比较 Clang      更新时间:2023-10-16

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

问题
如果复制/移动构造函数中有static_assert,那么即使可以省略复制/移动,标准是否也要求对其进行评估

以下内容应该会有所帮助。

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

#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段说(在注释中)"[…]即使调用实际上被实现"

3.2[basic.def.odr]第4段说:"每个程序都应该包含该程序中使用的每个非内联函数或变量的一个定义;不需要诊断。"

Ergo:应该实例化专门化,断言已经启动。

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

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

参考文献:

14.7.1.1…类模板专门化的隐式实例化导致声明的隐式实例,而不是类成员函数的定义、默认参数或异常规范的隐式示例…

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

move构造函数不会被调用。CCD_ 6在CCD_。最有可能的是,一些编译器在使用模板方法时(当您使用move构造函数时,您没有使用它)评估模板方法,而另一些编译器则在类模板实例化时(当首次使用X<int>时)评估。

我认为答案是:是的。

首先,staticassert强制构造函数从"definition"转换为声明。

关于下面的12.8节,我也不确定static_assert的模板性质。。。

(我为格式化道歉…)

c©ISO/IECN3242=11-00127声明[dcl.dcl]

2。声明是一个定义,除非它声明了一个函数而没有指定函数的主体(8.4),它包含外部说明符(7.1.1)或链接规范25(7.5),既不是初始化器也不是函数主体,它在类定义中声明了静态数据成员(9.4),它是一个类名声明(9.1),是一个不透明的枚举声明(7.2),或者是typedef声明(7.1.3)、using声明(7.3.3)、static_assert-declaration(第7条)、属性声明(第7款)、空声明(第七条)或using指令(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-在以下情况下,允许省略复制/移动操作,称为复制省略(可以将其组合以消除多个副本):--在具有类返回类型的函数中的返回语句中,当表达式是与函数返回类型具有相同cv不合格类型的非易失性自动对象(函数或catch子句参数除外)的名称时,可以通过将自动对象直接构造到函数的返回值中来省略复制/移动操作