2013-02-19 17 views
22

Il seguente codice viene compilato con clang (libC++) e non riesce con gcc (libstdC++). Perché gcc (libstdC++) si lamenta di un elenco di inizializzatori? Pensavo che l'argomento di ritorno stia usando una sintassi di inizializzazione uniforme.Restituzione di una tupla da una funzione utilizzando la sintassi di inizializzazione uniforme

std::tuple<double,double> dummy() { 
    return {2.0, 3.0}; 
} 

int main() { 
    std::tuple<double,double> a = dummy(); 
    return 0; 
} 

Error: line 22: converting to ‘std::tuple’ from initializer \ list would use explicit constructor ‘constexpr std::tuple<_T1, _T2>::tuple(_U1&\ &, _U2&&) [with _U1 = double; _U2 = double; = void; _T\ 1 = double; _T2 = double]’

Nota: GCC (libstdC++) (e clang (libC++)) accettare

std::tuple<double,double> dummy {1.0, 2.0}; 

non è forse lo stesso caso?

Update: questo è un libc estensione ++, vedere http://llvm.org/bugs/show_bug.cgi?id=15299 e anche rispondere da Howard Hinnant di seguito.

+0

Basta commentare la tua ultima domanda: No, non è lo stesso. Una dichiarazione di ritorno è un contesto di "inizializzazione della copia" mentre il tuo ultimo esempio è in realtà una "inizializzazione diretta". La differenza tra entrambi è che per l'inizializzazione della copia vengono considerati solo costruttori non espliciti. – sellibitze

+0

Anche Visual Studio 14 Update 3 lo accetta. – Liviu

risposta

32

A differenza di pair<>, la costruzione implicita di un tuple<> non è possibile purtroppo. Devi usare make_tuple():

#include <tuple> 

std::tuple<double,double> dummy() 
{ 
    return std::make_tuple(2.0, 3.0); // OK 
} 

int main() 
{ 
    std::tuple<double,double> a = dummy(); 
    return 0; 
} 

std::tuple ha un costruttore variadic, ma è contrassegnato come explicit. Pertanto, non può essere utilizzato in questa situazione, in cui un temporaneo deve essere implicitamente costruibile. Al comma 20.4.2 del C++ 11 standard:

namespace std { 
    template <class... Types> 
    class tuple { 
    public: 

     [...] 
     explicit tuple(const Types&...); // Marked as explicit! 

     template <class... UTypes> 
     explicit tuple(UTypes&&...);  // Marked as explicit! 

Per lo stesso motivo è illegale utilizzare la sintassi copia-inizializzazione per le tuple di inizializzazione:

std::tuple<double, double> a = {1.0, 2.0}; // ERROR! 
std::tuple<double, double> a{1.0, 2.0}; // OK 

o per costruire una tupla implicitamente quando si passa come argomento a una funzione:

void f(std::tuple<double, double> t) { ... } 
... 
f({1.0, 2.0}); // ERROR! 
f(make_tuple(1.0, 2.0)); // OK 

di conseguenza, se si costruisce il vostro std::tuple esplicitamente quando si ritorna in dummy(), non er la compilazione si verificherà:

#include <tuple> 

std::tuple<double,double> dummy() 
{ 
    return std::tuple<double, double>{2.0, 3.0}; // OK 
} 

int main() 
{ 
    std::tuple<double,double> a = dummy(); 
    return 0; 
} 
+2

Quindi clang è sbagliato accettare questo? Pensavo che il tipo di ritorno potesse essere inizializzato usando l'inizializzazione uniforme (non una lista di inizializzazione). – gnzlbg

+0

@gnzlbg: Non è una questione di compilatore, piuttosto di implementazione della libreria standard. Clang 3.2 non compila questo [qui] (http://liveworkspace.org/code/4hDrsS$7). –

+0

Ok, allora riformulerò la domanda. Quindi è errato in libC++ accettarlo? – gnzlbg

20

La risposta che dà Andy Prowl è corretta. Volevo commentare l'implementazione di libC++ e il formato dei commenti non mi permette di scegliere abbastanza spazio o opzioni di formattazione.

Daniel Krügler ed io abbiamo avuto una conversazione circa un anno fa su questo argomento, e mi ha convinto che questo problema era degno di inserire un'estensione in libC++ per ottenere esperienza sul campo. E finora il feedback è stato positivo. Tuttavia, voglio chiarire: non è così semplice come rimuovere explicit dal ctor explicit constexpr tuple(UTypes&&...).

Il piano di Daniel è quello di dare a tuple un costruttore che rispetti perfettamente la costruzione implicita/esplicita di ogni elemento. E se ogni elemento verrà costruito implicitamente da ogni argomento nell'elenco di inizializzazione, quindi la costruzione della tupla è implicita, altrimenti sarà esplicita.

Ad esempio:

Data:

#include <tuple> 

struct A 
{ 
}; 

struct B 
{ 
    B() = default; 
    B(A); 
}; 

struct C 
{ 
    C() = default; 
    explicit C(A); 
}; 

Poi:

std::tuple<> 
test0() 
{ 
    return {}; // ok 
} 

Non c'è molto da dire su quello. Ma anche questo è ok:

std::tuple<B> 
test1B() 
{ 
    return {A()}; // ok B(A) implicit 
} 

perché la conversione A-B è implicita. Tuttavia il seguente è un errore di compilazione:

std::tuple<C> 
test1C() 
{ 
    return {A()}; // error, C(A) is explicit 
} 

perché la conversione A-C è esplicito. Questa logica continua per le tuple a più elementi. Per una conversione implicita accada, ogni elemento deve avere una conversione implicita dalla lista degli argomenti:

std::tuple<A, B> 
test2B() 
{ 
    return {A(), A()}; // ok each element has implicit ctor 
} 

std::tuple<A, C> 
test2C() 
{ 
    return {A(), A()}; // error, C(A) is explicit 
} 

vorrei sottolineare: Si tratta di un'estensione libC++ in questo momento.

Aggiornamento

chico ha fatto un buon suggerimento che aggiorno questa risposta:

Dal momento che questa risposta è stata data, Daniel Krugler ha scritto un paper e lo ha presentato al comitato di C++ a Bristol lo scorso aprile. Sebbene il documento sia stato ben accolto, è stato rivisto troppo tardi nella settimana per votarlo nell'attuale bozza di lavoro.

Aggiornamento

la proposta di Daniel è ora parte del current working draft. L'implementazione di libC++ ora è impostata per diventare standard in questo senso per il prossimo standard C++ (C++ 17 speriamo).

+0

C'è un modo per disabilitare tali estensioni in libC++, in per mantenere il codice portatile? – bames53

+0

Non in questo momento. Tuttavia si noti che lo sforzo per port è un errore in fase di compilazione seguito da una soluzione banale. –

+0

Mantenete un elenco di estensioni libC++? Non ho visto nulla in una rapida occhiata alla sorgente di libC++ e ho capito che fai un bel po 'di sperimentazione. --- È vero che lo sforzo di porting una tantum è banale, ma se si sta facendo un'integrazione continua con i bot di sviluppo su una mezza dozzina di piattaforme è più comodo avere le proprie flag di piattaforma di lavoro. Non che io stia discutendo, questo merita un'attenzione speciale; se fosse veramente importante libC++ è open source e potrei lavorare su questo da solo ... – bames53

Problemi correlati