2015-01-15 9 views
9

Ho il codice della seguente struttura (che è ovviamente molto più complessa in realtà, specialmente "Base" è un three-liner, ma ho provato a catturare l'essenza di esso):Una istruzione 'using' si compila con g ++, fallisce la compilazione con clang

template <class T> 
class A {}; 

template <class T> 
class B { 
public: 
    B(){}; 
}; 

template <class T> 
class C : public B<A<T>> { 
public: 
    using Base = B<A<T>>; 
    using Base::B; 
}; 

static const C<int> c{}; 

il codice compila bene con g ++ tramite

g++ -c test.cpp -std=c++11 

Tuttavia, con clang ++ ricevo un messaggio di errore che non capisco

clang++ -c test.cpp -std=c++11 

test.cpp:14:14: error: dependent using declaration resolved to type without 'typename' using Base::B;

C'è qualcosa di sbagliato nel mio codice o si tratta di un bug in clang?

Nota: quando si scrive using B<A<T>>::B;, esso viene compilato correttamente con entrambi i compilatori, ma questa non è una soluzione reale al mio problema.

Edit: la versione 3.5.0 è clang, versione di gcc 4.9.2 è

+4

Quando si utilizzano i costruttori ereditari, il nome 'using' indica il costruttore, non il tipo, quindi' typename' è sbagliato. Ad ogni modo, 'usando Base :: Base' dovrebbe funzionare - c'è una regola speciale nella ricerca del nome che la risolve con il costruttore. http://coliru.stacked-crooked.com/a/5e01aeb4d26b312e –

+0

Grazie per la spiegazione. 'using Base :: Base' è una buona soluzione per il mio codice di produzione. Comunque, la domanda rimane se "usare Base :: B" è valido. Per me, la menzione sbagliata di 'typename' suggerisce che c'è qualcosa di sbagliato in clang.Non sono esperto in C++ comunque. –

+0

Sì, sembra un bug clang. Non c'è alcun motivo per cui "usare B > :: B;' e "usare Base :: B;" dovrebbe essere trattato in modo diverso. –

risposta

3

Questo caso è stato discusso in commissione il C++ (secondo Richard Smith https://llvm.org/bugs/show_bug.cgi?id=23107#c1) e le cose hanno ottenuto più chiaro ora:

using Base::B 

è non destinato ad essere codice valido.

Il seguente metodo è il modo corretto di esprimere costruttore eredità quando l'introduzione di una classe alias Base:

using Base::Base 

Tuttavia, i messaggi di errore prodotti dal clangore sono fuorvianti e si spera essere risolti come parte di questo bug report (https://llvm.org/bugs/show_bug.cgi?id=22242).

+0

3 anni dopo, il bug non è ancora risolto e il messaggio di errore è ancora fuorviante :( – Danra

3

Dalla sezione 12.1:

Constructors do not have names

Quindi le regole usuali per ricerca qualificati non si applicano. Invece si può contare solo sulla regola speciale per la ricerca costruttore (sezione 3.4.3.1):

In a lookup in which function names are not ignored and the nested-name-specifier nominates a class C :

— if the name specified after the nested-name-specifier, when looked up in C , is the injected-class-name of C , or

in a using-declaration (7.3.3) that is a member-declaration, if the name specified after the nested-name-specifier is the same as the identifier or the simple-template-id’s template-name in the last component of the nested-name-specifier,

the name is instead considered to name the constructor of class C .

Così si può certamente scrivere

using Base::Base; 

invece di

using Base::B; 

La versione originale dovrebbe funzionare sotto il primo punto elenco, ma i nomi delle classi iniettate diventano complicati quando sono coinvolti i modelli. Basta andare con la versione più semplice Base::Base, che in aggiunta è più leggibile. Chiunque lo veda immediatamente sa che stai nominando un costruttore.

Problemi correlati