【问题标题】:std::variant modification in constexprconstexpr 中的 std::variant 修改
【发布时间】:2018-12-04 17:08:37
【问题描述】:

考虑以下两个程序:

#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),第二个示例中的赋值应该表现得等同于emplace&lt;int&gt;(...),后者未声明constexpr ([variant.mod])。这是否意味着第二个示例格式错误,因为模板参数不能被评估为常量表达式,或者措辞是否允许/要求这种行为?

编辑:

根据 cmets,如果使用 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"

【问题讨论】:

  • 无法复制;您使用的是哪个版本的 g++ 和 clang++?
  • 如果你使用-stdlib=libc++会怎样
  • 看起来你的库有一个错误,因为variant&amp; operator=(variant&amp;&amp;) = default; 应该是constexpr variant&amp; operator=(variant&amp;&amp;) = default;
  • clang 通过 libc++ Demo 接受它。
  • 不是答案,但类似clang bug report

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


【解决方案1】:

这看起来像一个clang错误we can see from the libstdc++ variant header,移动赋值运算符确实没有标记为constexpr:

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

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

默认的类 X 的复制/移动赋值运算符 not defined as deleted 在 odr-used 时隐式定义 ([basic.def.odr]) (例如,当它被重载决议选择为 分配给其类类型的对象),当它需要常量时 评估([expr.const]),或者当它显式默认后 它的第一个声明。 隐式定义的复制/移动分配 运算符是 constexpr if

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

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

【讨论】:

  • 感谢您的回答。最初我想主要了解这两个程序是否格式正确,请参阅上面的多个问题EDIT:。如果可能的话,你能回答这些吗?
  • @user10605163 有很多问题讨论 constexpr 函数中的有效内容 for example here 它引用了完整的引号,而 f() 在那里的格式很好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-18
  • 2019-10-11
  • 1970-01-01
  • 1970-01-01
  • 2019-05-25
  • 2019-01-31
相关资源
最近更新 更多