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_assert
在 X<int>::X(X&&)
的实例化时计算。最有可能的是,某些编译器在使用时评估模板方法(当您使用移动构造函数时,但您不使用它),而其他编译器 - 在实例化 class 模板时(当您首次使用 X<int>
时)。
我相信答案是否定的。我的逻辑是这样的:
- 复制省略需要声明 copy/move 个构造函数,但不需要定义。
- 模板的成员函数定义不会被实例化,除非它们的定义是必需的。
- 如果定义未被实例化,则无法对其进行格式错误测试。
参考文献:
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."
因此:专业化应该被实例化,断言已经触发。
我在我的模板结构的移动构造函数中有一个 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_assert
在 X<int>::X(X&&)
的实例化时计算。最有可能的是,某些编译器在使用时评估模板方法(当您使用移动构造函数时,但您不使用它),而其他编译器 - 在实例化 class 模板时(当您首次使用 X<int>
时)。
我相信答案是否定的。我的逻辑是这样的:
- 复制省略需要声明 copy/move 个构造函数,但不需要定义。
- 模板的成员函数定义不会被实例化,除非它们的定义是必需的。
- 如果定义未被实例化,则无法对其进行格式错误测试。
参考文献:
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."
因此:专业化应该被实例化,断言已经触发。