std::variant constexpr 中的修改
std::variant modification in constexpr
考虑以下两个程序:
#include<variant>
#include<iostream>
constexpr auto f() {
using T = std::variant<bool, int>;
T t(false);
t = T(true);
return std::get<bool>(t);
}
template<auto V>
void print() { std::cout << V << "\n"; }
int main() {
print<f()>();
}
和
#include<variant>
#include<iostream>
constexpr auto f() {
using T = std::variant<bool, int>;
T t(false);
t = T(42);
return std::get<int>(t);
}
template<auto V>
void print() { std::cout << V << "\n"; }
int main() {
print<f()>();
}
GCC 编译这两个并输出预期的结果。在这两种情况下,Clang 都不会使用以下错误消息编译它们中的任何一个:
<source>:4:16: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
constexpr auto f() {
^
<source>:7:7: note: non-constexpr function 'operator=' cannot be used in a constant expression
t = T(42);
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/variant:1095:16: note: declared here
variant& operator=(variant&&) = default;
这两个程序的格式是否正确?如果不是,为什么?
此外,如果它们格式不正确,Clang 给出的错误消息是否合适?根据 [variant.assign] 移动赋值运算符应该是 constexpr
.
进一步根据(7.4) the assignment in the second example should behave equivalent to emplace<int>(...)
which is not declared constexpr
([variant.mod])。这是否意味着第二个例子是错误的,因为模板参数不能被评估为常量表达式,或者措辞 allow/require 是否有这种行为?
编辑:
根据评论,如果使用 libc++,Clang 似乎编译并输出正确的结果,并且错误仅发生在 libstdc++ 中。这是标准库和编译器不兼容吗?
在两种情况下都有效:
- GCC 8.2.0“-std=c++17”
- Clang 7.0.0“-std=c++17 -stdlib=libc++”
在这两种情况下都不起作用:
- Clang 7.0.0“-std=c++17”
这看起来像是一个 clang bug we can see from the libstdc++ variant header,移动赋值运算符确实没有标记为 constexpr:
variant& operator=(variant&&) = default;
但是默认和隐式定义的移动赋值运算符仍然可以是 constexpr,我们可以从 [class.copy.assign]p10 中看到这一点(强调我的):
A copy/move assignment operator for a class X that is defaulted and
not defined as deleted is implicitly defined when it is odr-used
([basic.def.odr]) (e.g., when it is selected by overload resolution to
assign to an object of its class type), when it is needed for constant
evaluation ([expr.const]), or when it is explicitly defaulted after
its first declaration. The implicitly-defined copy/move assignment
operator is constexpr if
- (10.1) X is a literal type, and
- (10.2) the assignment operator selected to copy/move each direct base class subobject is a constexpr function, and
- (10.3) for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that
member is a constexpr function.
据我所知,libstdc++ 实现应该适合所有这些情况,它是文字类型,它没有非静态数据成员,并且它所有基的赋值运算符也应该是 constexpr。
考虑以下两个程序:
#include<variant>
#include<iostream>
constexpr auto f() {
using T = std::variant<bool, int>;
T t(false);
t = T(true);
return std::get<bool>(t);
}
template<auto V>
void print() { std::cout << V << "\n"; }
int main() {
print<f()>();
}
和
#include<variant>
#include<iostream>
constexpr auto f() {
using T = std::variant<bool, int>;
T t(false);
t = T(42);
return std::get<int>(t);
}
template<auto V>
void print() { std::cout << V << "\n"; }
int main() {
print<f()>();
}
GCC 编译这两个并输出预期的结果。在这两种情况下,Clang 都不会使用以下错误消息编译它们中的任何一个:
<source>:4:16: error: constexpr function never produces a constant expression [-Winvalid-constexpr]
constexpr auto f() {
^
<source>:7:7: note: non-constexpr function 'operator=' cannot be used in a constant expression
t = T(42);
^
/opt/compiler-explorer/gcc-8.2.0/lib/gcc/x86_64-linux-gnu/8.2.0/../../../../include/c++/8.2.0/variant:1095:16: note: declared here
variant& operator=(variant&&) = default;
这两个程序的格式是否正确?如果不是,为什么?
此外,如果它们格式不正确,Clang 给出的错误消息是否合适?根据 [variant.assign] 移动赋值运算符应该是 constexpr
.
进一步根据(7.4) the assignment in the second example should behave equivalent to emplace<int>(...)
which is not declared constexpr
([variant.mod])。这是否意味着第二个例子是错误的,因为模板参数不能被评估为常量表达式,或者措辞 allow/require 是否有这种行为?
编辑:
根据评论,如果使用 libc++,Clang 似乎编译并输出正确的结果,并且错误仅发生在 libstdc++ 中。这是标准库和编译器不兼容吗?
在两种情况下都有效:
- GCC 8.2.0“-std=c++17”
- Clang 7.0.0“-std=c++17 -stdlib=libc++”
在这两种情况下都不起作用:
- Clang 7.0.0“-std=c++17”
这看起来像是一个 clang bug we can see from the libstdc++ variant header,移动赋值运算符确实没有标记为 constexpr:
variant& operator=(variant&&) = default;
但是默认和隐式定义的移动赋值运算符仍然可以是 constexpr,我们可以从 [class.copy.assign]p10 中看到这一点(强调我的):
A copy/move assignment operator for a class X that is defaulted and not defined as deleted is implicitly defined when it is odr-used ([basic.def.odr]) (e.g., when it is selected by overload resolution to assign to an object of its class type), when it is needed for constant evaluation ([expr.const]), or when it is explicitly defaulted after its first declaration. The implicitly-defined copy/move assignment operator is constexpr if
- (10.1) X is a literal type, and
- (10.2) the assignment operator selected to copy/move each direct base class subobject is a constexpr function, and
- (10.3) for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is a constexpr function.
据我所知,libstdc++ 实现应该适合所有这些情况,它是文字类型,它没有非静态数据成员,并且它所有基的赋值运算符也应该是 constexpr。