Странная языковая проблема - CWG 1581 :
Из пункта 15 [special] совершенно ясно, что специальные функции-члены определяются неявно только тогда, когда они используются в odr. Это создает проблему для константных выражений в неоцененных контекстах:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
Проблема здесь в том, что нам не разрешено неявно определять constexpr duration::duration(duration&&)
в этой программе, поэтому выражение в списке инициализаторов не является константным выражением (потому что оно вызывает функцию constexpr, которая не была определена), поэтому инициализированный скобками инициализатор содержит сужающее преобразование , так что программа плохо сформирована.
Если мы раскомментируем строку # 1, конструктор перемещения неявно определен, и программа действительна. Это жуткое действие на расстоянии крайне неудачно. Реализации расходятся по этому вопросу.
Вы можете прочитать остальную часть описания проблемы.
Решение по этому вопросу было принято в P0859 в Альбукерке в 2017 году (после поставки C ++ 17). Эта проблема была блокирующей для того, чтобы иметь возможность иметь constexpr std::swap
(решен в P0879 ) и a constexpr std::invoke
(решен в P1065 , который также имеет примеры CWG1581), оба для C ++ 20.
Самым простым для понимания примером здесь, на мой взгляд, является код из сообщения об ошибке LLVM, указанного в P1065:
template<typename T>
int f(T x)
{
return x.get();
}
template<typename T>
constexpr int g(T x)
{
return x.get();
}
int main() {
// O.K. The body of `f' is not required.
decltype(f(0)) a;
// Seems to instantiate the body of `g'
// and results in an error.
decltype(g(0)) b;
return 0;
}
CWG1581 все о когда constexpr функции - члены определены, и гарантирует разрешение , что они определены только при использовании. После P0859 вышеупомянутое хорошо сформировано (тип b
is int
).
Так как std::swap
и std::invoke
те, и другие должны полагаться на проверку функций-членов (переместить конструкцию / назначение в первом и вызовы оператора / суррогатные вызовы во втором), они оба зависели от решения этой проблемы.
std::is_move_constructible_v<T> && std::is_move_assignable_v<T>
istrue
. Этого не может быть, если специальные функции-члены еще не созданы.Причина
(из-за @NathanOliver)
Чтобы разрешить функцию
constexpr
подкачки, вы должны проверить - до создания экземпляра шаблона для этой функции - что тип подкачки является конструируемым при перемещении и назначаемым при перемещении. К сожалению, из-за языкового дефекта, исправленного только в C ++ 20, вы не можете проверить это, поскольку соответствующие функции-члены еще не были определены, поскольку это касается компилятора.Хронология
<algorithm>
функции какconstexpr
.constexpr std::swap()
а такжеconstexpr std::invoke()
- см. Объяснение выше.std::swap
и некоторые другие конструкции, и это принято в C ++ 17.std::swap()
constexpr после разрешения CWG-1581.std::invoke()
исправление constexpr .Ваш конкретный случай
Вы можете использовать
constexpr
подкачку, если вы не проверяете возможность перемещения и присваивания перемещения, а вместо этого непосредственно проверяете некоторые другие особенности типов, которые гарантируют это, в частности. например, только примитивные типы и нет классов или структур. Или, теоретически, вы можете отказаться от проверок и просто иметь дело с любыми ошибками компиляции, с которыми вы можете столкнуться, и с нестабильным переключением поведения между компиляторами. В любом случае, не заменяйтеstd::swap()
на подобные вещи.источник