Dziwny problem językowy to CWG 1581 :
Klauzula 15 [special] jest całkowicie jasne, że specjalne funkcje składowe są domyślnie zdefiniowane tylko wtedy, gdy są używane odr. Stwarza to problem dla stałych wyrażeń w nieocenionych kontekstach:
struct duration {
constexpr duration() {}
constexpr operator int() const { return 0; }
};
// duration d = duration(); // #1
int n = sizeof(short{duration(duration())});
Problem polega na tym, że nie wolno nam niejawnie definiować constexpr duration::duration(duration&&)
w tym programie, więc wyrażenie na liście inicjalizatora nie jest wyrażeniem stałym (ponieważ wywołuje funkcję constexpr, która nie została zdefiniowana), więc inicjator stężeń zawiera zawężającą się konwersję , więc program jest źle sformułowany.
Jeśli odkomentujemy wiersz nr 1, konstruktor ruchu jest domyślnie zdefiniowany, a program jest prawidłowy. Ta upiorna akcja na odległość jest wyjątkowo niefortunna. Implementacje różnią się w tym punkcie.
Możesz przeczytać resztę opisu problemu.
Rozwiązanie tego problemu zostało przyjęte w P0859 w Albuquerque w 2017 r. (Po wysłaniu C ++ 17). Problem ten blokował możliwość posiadania constexpr std::swap
(rozwiązanego w P0879 ) i constexpr std::invoke
(rozwiązanego w P1065 , który również zawiera przykłady CWG1581), zarówno dla C ++ 20.
Moim zdaniem najprostszym do zrozumienia przykładem jest kod z raportu o błędach LLVM wskazanego w 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 wszystko o kiedy constexpr funkcje składowe są zdefiniowane, a rozdzielczość, zapewnia, że są one jedynie zdefiniowane podczas eksploatacji. Po P0859 powyższe jest dobrze uformowane (typ b
jest int
).
Od std::swap
istd::invoke
oba mają polegać na sprawdzanie funkcji składowych (budowa MOVE / zadanie w dawnej i operator call / zastępcze połączeń w ostatni), oboje zależała od rozwiązania tego problemu.
std::is_move_constructible_v<T> && std::is_move_assignable_v<T>
totrue
. Nie może się to zdarzyć, jeśli specjalne funkcje składowe nie są jeszcze generowane.Powód
(z powodu @NathanOliver)
Aby zezwolić na
constexpr
funkcję zamiany, musisz sprawdzić - przed utworzeniem wystąpienia szablonu dla tej funkcji - czy typ zamiany jest możliwy do utworzenia i przypisania do ruchu. Niestety, z powodu wady języka rozwiązanej tylko w C ++ 20, nie możesz sprawdzić, ponieważ odpowiednie funkcje składowe mogły nie zostać jeszcze zdefiniowane, jeśli chodzi o kompilator.Chronologia
<algorithm>
funkcje jakoconstexpr
.constexpr std::swap()
równieżconstexpr std::invoke()
- patrz wyjaśnienie powyżej.std::swap
i kilka innych konstrukcji, co zostało zaakceptowane w C ++ 17.std::swap()
constexpr po rezolucji CWG-1581.std::invoke()
.Twój konkretny przypadek
Możesz użyć
constexpr
zamiany, jeśli nie sprawdzasz możliwości konstruowania ruchów i przypisywania ruchów, ale raczej bezpośrednio sprawdzasz inne cechy typów, które zapewniają to w szczególności. np. tylko prymitywne typy i brak klas lub struktur. Teoretycznie możesz zrezygnować z kontroli i po prostu poradzić sobie z napotkanymi błędami kompilacji oraz niestabilnym przełączaniem się między kompilatorami. W każdym razie nie zastępujstd::swap()
tego rodzaju rzeczami.źródło