Рассмотрим следующие две программы:
#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?Согласно [variable.assign] оператор присваивания перемещения должен быть constexpr
.
Кроме того, согласно (7.4) назначение во втором примере должно вести себя эквивалентнона emplace<int>(...)
, который не объявлен constexpr
( [вариант.мод] ).Означает ли это, что второй пример неверно сформирован, потому что аргумент шаблона не может быть оценен как константное выражение, или формулировка допускает / требует такого поведения?
РЕДАКТИРОВАТЬ:
На основании комментариев кажетсячто Clang компилирует и выдает правильные результаты, если используется libc ++ и ошибка возникает только с 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"