5

Il seguente esempio semplificato viene compilato in gcc e Visual Studio, ma non riesce in clang!?Sovraccarico dell'operatore C++ e namespace associato

namespace N 
{ 
    struct A {}; 

    template <typename T> 
    double operator+ (T a, double d) {return d;} 

    template <typename T> 
    double operator+ (double d, T a) {return d;} 
} 

void test() 
{ 
    N::A a; 
    double x; 

    double y = a + x; 
    double z = x + a; 
} 

come la vedo io, la su modelli operator+ nel namespace N dovrebbe trovare da ADL.

Perché clang non è d'accordo? È un bug in clang o negli altri compilatori?

Ecco l'errore di compilazione da clang 3.5.1 (testato su coliru), non capisco qual è il problema qui ...

10 : error: overloaded 'operator+' must have at least one parameter of class or enumeration type 
double operator+ (double d, T a) {return d;} 
^ 
18 : note: in instantiation of function template specialization 'N::operator+' requested here 
double y = a + x; 
^ 

7 : error: overloaded 'operator+' must have at least one parameter of class or enumeration type 
double operator+ (T a, double d) {return d;} 
^ 
19 : note: in instantiation of function template specialization 'N::operator+' requested here 
double z = x + a; 
^ 

2 errors generated. 
Compilation failed 

L'esempio è semplificato dal codice vita reale, di corso. L'intenzione è che qualsiasi classe definita all'interno del namespace N abbia un operatore sovraccarico + con un doppio.

+0

Da quell'errore, sembra essere stato trovato da ADL. Non sei sicuro di cosa si stia lamentando anche se – xcvr

+0

Nota che il tuo programma di test ha un comportamento indefinito, dal momento che 'x' non è inizializzato. –

+0

@KerrekSB * Stai * riproducendo perché stai usando gcc mentre puoi compilarlo. [Clang can not] (http://coliru.stacked-crooked.com/a/bd4cfc7eb1cc02d7). – 0x499602D2

risposta

4

Questo è causato da due differenti aspetti: CWG CWG issue 2052 e CWG issue 1391.

In primo luogo, CWG 1391. Su incontrando x + a, il solito nome di ricerca trova, tra le altre sovraccarichi,

template <typename T> double operator+ (T, double); 

Template argomento deduzione viene eseguita da corrispondenza T alla tipologia delle ss di +, che è double , quindi questo deduce T da double. Il tipo del secondo parametro non contiene parametri di template, quindi non è considerato secondo le regole attuali. Per sicurezza, N::A non può essere convertito in double, quindi la specializzazione risultante non è praticabile, ma le regole attuali dicono che la deduzione degli argomenti del modello non interessa questo; che sarà gestito nella risoluzione di sovraccarico.

La proposta di deliberazione CWG 1391, tra le altre cose, aggiunge un nuovo paragrafo alla norma:

Se deduzione riesce per tutti i parametri che contengono template-parametri che partecipano al modello argomento deduzione, e tutti gli argomenti del modello sono esplicitamente specificati, dedotti o ottenuti dagli argomenti del modello di default, i parametri rimanenti sono quindi confrontati con gli argomenti corrispondenti. Per ogni restante parametro P con un tipo che era non-dipendente prima sostituzione argomenti template esplicitamente specificati, se il corrispondente tesi A non può essere convertito in modo implicito P, deduzione fallisce. [Nota: I parametri con tipi dipendenti in cui non template-parametri partecipano detrazione template argomento, ei parametri che divennero non-dipendente dovuta alla sostituzione della esplicitamente specificati modello argomenti, sarà controllato durante la risoluzione di sovraccarico. - nota fine]

In altre parole, se un argomento (a nel nostro caso) corrispondente ad un parametro non dipendente (double) non può essere convertito nel tipo del parametro, detrazione semplicemente sicuro. Quindi nel nostro caso, la deduzione degli argomenti del template post-CWG1391 fallirà per questo sovraccarico, e tutto andrebbe bene.

Clang implementa le regole attuali, tuttavia, in modo deduzione riesce con T = double, si verifica la sostituzione, e che incontriamo CWG 2052. Citando l'interessante resoconto da Richard Smith (un dev Clang):

In un esempio come

struct A { operator int(); }; 
    template<typename T> T operator<<(T, int); 
    void f(A a) { 1 << a; } 

argomento template detrazione riesce per il modello dell'operatore, producendo la firma operator<<(int,int). Il dichiarazione risultante viene sintetizzato e ha aggiunto al set di sovraccarico, per 14.8.3 [temp.over] paragrafo 1. Tuttavia, questo viola il requisito della 13.5 [over.oper] comma 6,

An la funzione dell'operatore deve essere una funzione membro non statico o essere una funzione non membro che ha almeno un parametro il cui tipo è una classe, un riferimento a una classe, un'enumerazione o un riferimento a un'enumerazione .

Questo non è un contesto SFINAE, quindi il programma è mal formato, piuttosto rispetto alla selezione dell'operatore integrato.

In questo caso, non c'è conversione, in modo che il dedotto operator+(double, double) è in realtà non è praticabile, ma i candidati non vitali non sono eliminati fino a quando si è creato il set di candidato, e qui la costruzione del set candidato sta causando un errore hardware .

La proposta di risoluzione per CWG 2052 renderà questo caso SFINAE - anche facendo funzionare il codice originale. Il problema è che anche qui Clang sta implementando la versione attuale dello standard.

+0

Perché l'operatore '+ (doppio, doppio)' può essere dedotto del tutto nella situazione dell'OP? –

+0

@KerrekSB Dato 'modello doppio operatore + (T, doppio); doppio x; N :: A a; 'e' x + a', 'T' è dedotto da lhs, cioè' double'. Ovviamente non c'è conversione da 'N :: A' a' double' per il rhs, ma la deduzione dell'argomento template non interessa; è rimasto per la risoluzione di sovraccarico. –

+0

Foro interessante nello standard. Grazie per la spiegazione. Immagino che Clang avesse ragione, e GCC/VC avranno ragione. – xcvr

2

Potrebbe essersi lamentato perché Tpotrebbe essere non essere una classe in quella definizione. E non è consentito ridefinire lo standard operator+ per i tipi aritmetici IIRC. Nel tuo esempio, non c'è nulla che limiti T ad essere N::A per esempio.

L'aggiunta di typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}> sembra risolvere il problema. Visual Studio e GCC potrebbero essere un po 'più rilassati/pigri rispetto a questa restrizione.

namespace N 
{ 
    struct A {}; 

    template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>> 
    double operator+ (T a, double d) {return d;} 

    template <typename T, typename = std::enable_if_t<std::is_class<T>{} || std::is_enum<T>{}>> 
    double operator+ (double d, T a) {return d;} 
} 

void test() 
{ 
    N::A a; 
    double x; 

    double y = a + x; 
    double z = x + a; 
} 
+0

Non penso che il problema dovrebbe essere un problema fino alla creazione di istanze. Ad esempio, se tu [rimuovi uno dei sovraccarichi] (http://coliru.stacked-crooked.com/a/a361da7a886c1a26) clang non si lamenterà. – 0x499602D2

+0

@ 0x499602D2 Vero. Qui sta succedendo qualcosa di strano. Se lasci nell'altra aggiunta, http://coliru.stacked-crooked.com/a/c3dd251ee27581b3, ottieni ancora l'errore. – xcvr

+0

I messaggi di errore (sia quelli originali che quelli coliru) indicano che sta creando un'istanza delle funzioni con 'T = double' in quelle istanze in cui non riesce a compilare.'in istanziazione della specializzazione del modello di funzione 'N :: operator + ' richiesto qui ' – xcvr