Grałem około z auto
w std::pair
. W poniższym kodzie funkcja f
powinna zwracać std::pair
typy zależne od parametru szablonu.
Przykład roboczy:
PRZYKŁAD 1
template <unsigned S>
auto f()
{
if constexpr (S == 1)
return std::pair{1, 2}; // pair of ints
else if constexpr (S == 2)
return std::pair{1.0, 2.0}; // pair of doubles
else
return std::pair{0.0f, 0.0f}; // pair of floats
}
Działa to z gcc 9.2, gcc 10.0, clang 9.0 i clang 10.0.
Następnie chciałem jawnie napisać typ zwracany std::pair
ze względów przejrzystości:
PRZYKŁAD 2
template <unsigned S>
std::pair<auto, auto> f()
{
if constexpr (S == 1)
return {1, 2};
/* ... */
}
Zarówno gcc 9.2 / 10.0, jak i clang 9.0 / 10.0 nie skompilowały tego.
gcc 9.2
error: invalid use of 'auto'
error: template argument 1 is invalid // first argument (auto) of std::pair
error: template argument 2 is invalid // second argument (auto) of std::pair
error: cannot convert '<brace-enclosed initializer list>' to 'int' in return
Po ostatnim komunikacie o błędzie gcc 9.2 wydaje się wierzyć, że std::pair<auto, auto>
jest to int
. Jak można to wyjaśnić?
gcc 10.0
error: returning initializer list
Ten błąd jest zrozumiały, ale spodziewałem się, że std::pair
zostanie wywołany konstruktor, czy też czegoś mi brakuje?
klang 9.0 i 10.0
'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'
Ok, clangowi się to nie podoba. Na podstawie drugiego komunikatu o błędzie wydaje się, że clang uważa również, że typem zwrotu jest int
.
Wreszcie, aby naprawić błąd uzyskany podczas kompilacji z gcc 10.0, postanowiłem zwrócić std::pair
jawnie:
PRZYKŁAD 3
template <unsigned S>
std::pair<auto, auto> f()
{
if constexpr (S == 1)
return std::pair{1, 2};
/* ... */
}
klang 9.0 i 10.0
Tak jak poprzednio, ale z dodatkowym:
no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'
Tutaj klang nadal myśli, że wracamy int
?
gcc 9.2
Tak samo jak ostatnio.
gcc 10.0
To działa!
Wydaje mi się, że niektóre funkcje wciąż muszą zostać zaimplementowane lub w jednej z opisanych powyżej sytuacji, czy istnieje kompilator, który jest poprawny, a drugi źle? Moim zdaniem przykład 2 powinien działać. A może nie?
auto x = {1, 2};
działa, ale tylko wtedy, gdy wszystkie typy są takie same.int
. To nieint
jest symbol zastępczy w komunikatach o błędach; kompilator naprawdę myśli, że toint
. (Aby to wyjaśnić, gcc powinien był kiedyś powiedzieć „zakładając int”.)std::pair __f{1,2};
działa.std::optional f() { return 4; }
pracy.