constexpr中的std :: variant修改

use*_*163 7 c++ variant constexpr c++17

考虑以下两个程序:

#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()>();
}
Run Code Online (Sandbox Code Playgroud)

#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()>();
}
Run Code Online (Sandbox Code Playgroud)

海湾合作委员会汇编这两项并输出预期结果.在两种情况下,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;
Run Code Online (Sandbox Code Playgroud)

这两个项目是否格局良好?如果没有,为什么?

另外,如果它们格式不正确,Clang会给出错误消息吗?根据[variant.assign],移动赋值运算符应该是constexpr.

此外,根据(7.4),第二个例子中的赋值应该等同于emplace<int>(...)未声明的赋值constexpr([variant.mod]).这是否意味着第二个示例格式错误,因为模板参数不能被评估为常量表达式,或者措辞是否允许/要求此行为?

编辑:

根据评论,如果使用libc ++,Clang似乎编译并输出正确的结果,并且仅在libstdc ++中发生错误.这是标准库和编译器之间的不兼容吗?

https://godbolt.org/上:

适用于两种情况:

  • GCC 8.2.0"-std = c ++ 17"
  • Clang 7.0.0"-std = c ++ 17 -stdlib = libc ++"

在任何一种情况下都不起作用:

  • Clang 7.0.0"-std = c ++ 17"

Sha*_*our 2

这看起来像是一个 clang bug,我们可以从 libstdc++ 变体标头中看到,移动赋值运算符确实没有标记为 constexpr:

variant& operator=(variant&&) = default;
Run Code Online (Sandbox Code Playgroud)

但默认且隐式定义的移动赋值运算符仍然可以是 constexpr,我们可以从[class.copy.assign]p10中看到这一点(重点是我的):

默认且未定义为已删除的类 X 的复制/移动赋值运算符在 odr 使用时 ([basic.def.odr]) 被隐式定义(例如,当重载决议选择它来分配给其类类型的对象),当需要常量求值([expr.const])时,或者在第一次声明后显式默认它时。隐式定义的复制/移动赋值运算符是 constexpr 如果

  • (10.1) X 是文字类型,并且
  • (10.2) 选择用于复制/移动每个直接基类子对象的赋值运算符是 constexpr 函数,并且
  • (10.3) 对于 X 的类类型(或其数组)的每个非静态数据成员,选择复制/移动该成员的赋值运算符是 constexpr 函数。

据我所知,libstdc++ 实现应该适合所有这些情况,它是文字类型,它没有非静态数据成员,并且其所有基数的赋值运算符也应该是 constexpr。