Eu estava brincando com auto
no std::pair
. No código abaixo, a função f
deve retornar um std::pair
dos tipos que dependem de um parâmetro de modelo.
Um exemplo de trabalho:
EXEMPLO 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
}
Isso funciona com o gcc 9.2, gcc 10.0, clang 9.0 e clang 10.0.
Em seguida, eu queria escrever explicitamente o tipo de retorno como um std::pair
motivo de clareza:
EXEMPLO 2
template <unsigned S>
std::pair<auto, auto> f()
{
if constexpr (S == 1)
return {1, 2};
/* ... */
}
O gcc 9.2 / 10.0 e o clang 9.0 / 10.0 falharam ao compilar isso.
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
Da última mensagem de erro, o gcc 9.2 parece acreditar que std::pair<auto, auto>
é um int
. Como isso pode ser explicado?
gcc 10.0
error: returning initializer list
Esse erro é compreensível, no entanto, eu esperava que o construtor std::pair
fosse invocado ou há algo que estou faltando aqui?
clang 9.0 e 10.0
'auto' not allowed in template argument
excess elements in scalar initializer
no matching function for call to 'f'
Ok, clang não gosta de nada disso. Na segunda mensagem de erro, parece que o clang também acredita que é o tipo de retorno int
.
Por fim, para corrigir o erro obtido na compilação com o gcc 10.0, decidi retornar um std::pair
explicitamente:
EXEMPLO 3
template <unsigned S>
std::pair<auto, auto> f()
{
if constexpr (S == 1)
return std::pair{1, 2};
/* ... */
}
clang 9.0 e 10.0
O mesmo de antes, mas com um adicional:
no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'
Aqui clang ainda pensa que estamos retornando um int
?
gcc 9.2
O mesmo de antes.
gcc 10.0
Funciona!
Eu acho que alguns recursos ainda precisam ser implementados, ou em uma das situações descritas acima, existe um compilador certo e o outro errado? Na minha opinião, o exemplo 2 deve funcionar. Ou não deveria?
auto x = {1, 2};
funciona, mas apenas se todos os tipos forem iguais.int
. Nãoint
é um espaço reservado nas mensagens de erro; o compilador realmente pensa que é umint
. (Para deixar isso mais claro, o gcc provavelmente deveria ter dito "assumindo int" em algum momento.)std::pair __f{1,2};
funciona.std::optional f() { return 4; }
trabalho.