constexpr中的std :: variant修改

时间:2018-12-04 17:08:38

标签: c++ c++17 constexpr variant

请考虑以下两个程序:

#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给出的错误消息是否合适?根据{{​​3}},移动分配运算符应为constexpr

此外,根据[variant.assign],第二个示例中的赋值应等效于未声明为emplace<int>(...)(7.4))的constexpr。这是否暗示第二个示例格式错误,因为无法将模板参数评估为常量表达式,或者措辞是否允许/要求此行为?

编辑:

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

[variant.mod]上:

在两种情况下均有效:

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

在两种情况下均无效:

  • Clang 7.0.0“ -std = c ++ 17”

1 个答案:

答案 0 :(得分:2)

这看起来像一个bug虫we can see from the libstdc++ variant header,移动分配运算符的确没有标记为constexpr:

variant& operator=(variant&&) = default;

但是默认的和隐式定义的移动赋值运算符仍然可以是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。