6

Supponiamo da qualche parte nel mio codice è una funzione foo con un parametro di riferimento universale, che non posso cambiare:risoluzione C++ sovraccarico con funzione di modello di riferimento universale, che non può essere cambiato

template<typename T> 
auto foo(T&& t) { std::cout<<"general version"<<std::endl; } 

Ora voglio sovraccarico foo per una determinata classe A e assicurarsi che per ogni qualificatore e tipo di riferimento di A venga chiamato il sovraccarico. Per questo posso bruta forcely fornire un sovraccarico per tutte le qualifiche possibili (ignorare volatile per ora):

auto foo(A & a) { std::cout<<"A&"<<std::endl; } 
auto foo(A const& a) { std::cout<<"A const&"<<std::endl; } 
auto foo(A && a) { std::cout<<"A &&"<<std::endl; } 
auto foo(A const&& a) { std::cout<<"A const&&"<<std::endl; } 

Demo. Questo tuttavia scala molto male per ulteriori parametri.

alternativa, posso passare dal valore, che sembra catturare così tutti i casi precedenti:

auto foo(A a) { std::cout<<"A"<<std::endl; } 

Demo. Ora però è necessario copiare un oggetto di grandi dimensioni (- almeno in linea di principio).

C'è un modo elegante per risolvere questi problemi?

Ricordare che non è possibile modificare la funzione di riferimento universale, quindi SFINAE e simili non sono possibili.

+0

sta scrivendo una funzione wrapper che inoltra argomenti per il corretto funzionamento e l'utilizzo che, invece di una soluzione ragionevole per voi? – TartanLlama

+0

AFAIK il compilatore cerca sempre l'opzione più specifica. Cosa succede se si aggiunge un enable_if > su "overload"? –

+1

@TartanLlama: non proprio. Nel codice la funzione corrispondente è 'operator &&' (ho appena preso 'foo' come un semplice esempio). È una biblioteca e questo è troppo sbagliato. – davidhigh

risposta

7

Onestamente, penso che tu sia sfortunato qui. Gli approcci tipici falliscono tutti. You can do ...

SFINAE?

template <typename T> auto foo(T&&); 
template <typename T, 
      typename = only_if_is<T, A>> 
auto foo(T&&); 

foo(A{}); // error: ambiguous 

Scrivere una classe che prende un riferimento di l-or-rvalue?

template <typename T> lref_or_ref { ... }; 

template <typename T> auto foo(T&&); 
auto foo(lref_or_ref<A>); 

foo(A{}); // calls general, it's a better match 

Il meglio che si possa fare è introdurre una funzione di inoltro tramite un selettore:

template <int I> struct chooser : chooser<I - 1> { }; 
template <> struct chooser<0> { }; 

template <typename T> 
auto bar(T&& t, chooser<0>) { 
    // worst-option, general case 
    foo(std::forward<T>(t)); 
} 

template <typename T, 
      typename = only_if_is<T, A>> 
auto bar(T&& t, chooser<1>) { 
    // A-specific 
} 

template <typename T> 
auto bar(T&& t) { 
    bar(std::forward<T>(t), chooser<20>{}); 
} 

Ma voi hanno citato in a comment che questo non funziona neanche per te. Quindi immagino che la tua unica opzione sia: scrivere una proposta al comitato degli standard!


Fondamentalmente, c'è speranza! Se concetti ottiene adottate (buon punto, TartanLlama!):

template <typename T> 
    requires IsForwardRefTo<T, A> 
auto foo(T&& t) { 
    // since this is more constrained than the generic forwarding reference 
    // this one should be preferred for foo(A{}) 
} 
+0

E 'possibile ottenere almeno gli lvalue con 'modello auto foo (T & t);', ma non sembra esserci un modo per farlo per rvalues. A meno che non si utilizzino concetti, cioè (dove è possibile definire un parametro di riferimento del valore assoluto dedotto), ma in tal caso è possibile utilizzare la soluzione più semplice per ottenere entrambi tramite un riferimento di inoltro vincolato. – dyp

Problemi correlati