std :: pair <auto, auto> tipo di ritorno


16

Stavo giocando con autodentro std::pair. Nel codice seguente, la funzione fdovrebbe restituire un std::pairtipo che dipende da un parametro template.

Un esempio funzionante:

ESEMPIO 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
}

Funziona con gcc 9.2, gcc 10.0, clang 9.0 e clang 10.0.

Successivamente, ho voluto scrivere esplicitamente il tipo restituito come a std::pairmotivi di chiarezza:

ESEMPIO 2

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return {1, 2};
    /* ... */
}

Sia gcc 9.2 / 10.0 che clang 9.0 / 10.0 non sono riusciti a compilare questo.

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

Dall'ultimo messaggio di errore, gcc 9.2 sembra credere che std::pair<auto, auto>sia un int. Come può essere spiegato?

gcc 10.0

error: returning initializer list

Questo errore è comprensibile, tuttavia, mi aspettavo che il costruttore std::pairfosse invocato o c'è qualcosa che mi manca qui?

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, a Clang non piace niente di tutto questo. Dal secondo messaggio di errore, sembra che Clang creda anche che sia il tipo restituito int.

Infine, per correggere l'errore ottenuto compilando con gcc 10.0, ho deciso di restituire std::pairesplicitamente un :

ESEMPIO 3

template <unsigned S>
std::pair<auto, auto> f()
{
    if constexpr (S == 1)
        return std::pair{1, 2};
    /* ... */
}

clang 9.0 e 10.0

Come prima, ma con un ulteriore:

no viable conversion from returned value of type 'std::pair<int, int>' to function return type 'int'

Qui Clang pensa ancora che stiamo tornando un int?

gcc 9.2

Come prima.

gcc 10.0

Funziona!

Immagino che alcune funzionalità debbano ancora essere implementate, o in una delle situazioni sopra descritte, c'è un compilatore che è giusto e l'altro sbagliato? Secondo me, l'esempio 2 dovrebbe funzionare. O non dovrebbe?

Risposte:


23

La sintassi:

std::pair<auto, auto> f() { return std::pair(1, 2); }
~~~~~~~~~~~~~~~~~~~~~

Faceva parte del TS Concepts originale ma non era incluso nella proposta Concepts che fa parte di C ++ 20. Pertanto, gli unici tipi di segnaposto in C ++ 20 sono auto(e loro varianti simili auto**) decltype(auto), e segnaposto vincolati ( Concept autoe loro varianti). Questo tipo di segnaposto nidificato sarebbe molto utile, ma non fa parte di C ++ 20, pertanto la dichiarazione di funzione non è corretta.

Ora, gcc lo consente perché gcc ha implementato Concepts TS e immagino che abbiano deciso di mantenere questa funzionalità. clang non ha mai implementato il TS, quindi non lo fa.

In entrambi i casi, questo:

std::pair<auto, auto> f() { return {1, 2}; }

Sarebbe sempre mal formato. Il significato della sintassi è che deduciamo il tipo restituito e quindi richiediamo che corrisponda pair<T, U>per alcuni tipi Te U. Fondamentalmente stiamo cercando di invocare la funzione inventata:

template <typename T, typename U>
void __f(std::pair<T, U>);

__f({1, 2}); // this must succeed

Ma non è possibile dedurre un tipo da {1, 2}- un elenco di avvio non ha un tipo. Forse questo è qualcosa che dovrebbe essere esplorato (poiché è facile da capire almeno in un caso semplice come questo), ma non è mai stato permesso. Quindi rifiutarlo è corretto in entrambi i modi.

Infine:

gcc 9.2 sembra credere che std::pair<auto, auto>sia un int. Come può essere spiegato?

Per qualche motivo (probabilmente a causa della nostra eredità C con implicito int), quando gcc non riconosce o comprende un tipo, utilizza solo intcome segnaposto nei messaggi di errore. Questo è super confuso, perché ovviamente è gcc che è venuto fuori inte non il codice sorgente. Ma è così.


L'elenco "braced-init-list non ha un argomento di tipo" non mi è chiaro. std :: pair <int, int> f () {return {1,2}; } funziona e {1,2} non ha alcun tipo (invoca il costruttore di std :: pair <int, int> come ho capito). Forse con <auto, auto>, il compilatore non può dedurre i tipi di 1 e 2 nell'elenco di inizializzazione {1, 2}?
mfnx,

@mfnx Non ha un argomento di tipo , ma non ha un tipo. gli elenchi init con parentesi graffe possono essere utilizzati solo in determinate situazioni, come l'inizializzazione di un tipo noto. Ma non possono essere usati in deduzione, perché non hanno tipo. Tranne il auto x = {1, 2};funzionamento, ma solo se tutti i tipi sono uguali.
Barry,

2
La maggior parte dei compilatori, invece di limitarsi a fermarsi al primo errore, prova a recuperarlo in modo da poter segnalare ulteriori errori. Questo generalmente significa presumere che tutto ciò che non è analizzabile sia un int. Non intè un segnaposto nei messaggi di errore; il compilatore pensa davvero che sia un int. (Per chiarire questo, gcc avrebbe probabilmente dovuto dire "assumendo int" ad un certo punto.)
Raymond Chen,

2
Si noti che un'altra possibile strada per l'estensione sarebbe quella di consentire la deduzione dell'argomento del modello di classe per i tipi di ritorno, poiché std::pair __f{1,2};funziona.
Davis Herring,

2
@DavisHerring non vorrei veramente avere std::optional f() { return 4; }il lavoro.
Barry,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.