Странная языковая проблема - 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
те, и другие должны полагаться на проверку функций-членов (переместить конструкцию / назначение в первом и вызовы оператора / суррогатные вызовы во втором), они оба зависели от решения этой проблемы.