std :: вариант модификации в constexpr - PullRequest
0 голосов
/ 04 декабря 2018

Рассмотрим следующие две программы:

#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"

1 Ответ

0 голосов
/ 12 декабря 2018

Это похоже на ошибку clang , которую мы можем видеть из варианта заголовка libstdc ++ , что оператор присваивания перемещения действительно не помечен как constexpr:

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

, но является заданным по умолчанию и неявно определенным движениемоператор присваивания все еще может быть constexpr, мы можем видеть это из [class.copy.assign] p10 ( выделение шахты ):

Копирование / перемещениеОператор присваивания для класса X, который по умолчанию и не определен как удаленный, неявно определяется, когда он используется odr ([basic.def.odr]) (например, когда он выбирается с помощью разрешения перегрузки для назначения объекту своего классаtype), когда это необходимо для константной оценки ([expr.const]), или когда оно явно установлено по умолчанию после первого объявления.Неявно определенный оператор присваивания копирования / перемещения является constexpr, если

  • (10.1) X является литеральным типом, а
  • (10.2) выбранным оператором присваиваниядля копирования / перемещения каждого подобъекта прямого базового класса используется функция constexpr, и
  • (10.3) для каждого не статического члена данных X, который имеет тип класса (или его массив), оператор присваивания, выбранный для копирования/ переместить этот член в функцию constexpr.

Из того, что я могу сказать, реализация libstdc ++ должна соответствовать всем этим случаям, это литеральный тип, он не имеет нестатических членов-данныхи оператор присваивания для всех его баз также должен быть constexpr.

...