6

irgendwo in meinem Code Angenommen, eine Funktion foo mit einem Universal-Referenzparameter, , die ich nicht ändern kann:C++ Überladungsauflösung mit Funktionsvorlage Universal-Referenz, die nicht geändert werden kann

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

Jetzt mag ich Überlastung foo für eine gegebene Klasse A, und stellen Sie sicher, dass für jeden Qualifier und Referenztyp von A die Überlast aufgerufen wird. Dafür ich Brute-forcely kann eine Überlastung für alle möglichen Qualifikationen bieten (ignorieren volatile jetzt):

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. Dies skaliert jedoch sehr schlecht für weitere Parameter.

Alternativ kann ich nach Wert übergeben, die alle zu erfassen scheint auch die vorherigen Fälle:

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

Demo. Jetzt müssen jedoch große Objekte kopiert werden (- zumindest im Prinzip).

Gibt es eine elegante Lösung für diese Probleme?

Denken Sie daran, dass ich die universelle Referenzfunktion nicht ändern kann, so dass SFINAE und dergleichen keine Möglichkeit ist.

+0

Ist Schreiben eine Wrapper-Funktion, die Argumente für die korrekte Funktion leitet und die Verwendung dieser stattdessen eine vernünftige Lösung für Sie? – TartanLlama

+0

AFAIK der Compiler sucht immer nach der spezifischsten Option. Was ist, wenn Sie ein enable_if > auf der "Überlast" hinzufügen? –

+1

@TartanLlama: nicht wirklich. Im Code ist die entsprechende Funktion 'operator &&' (ich habe 'foo' als einfaches Beispiel genommen). Es ist eine Bibliothek und das ist zu fehlerhaft. – davidhigh

Antwort

7

Ehrlich gesagt, ich denke, Sie haben Pech hier. Die typischen Ansätze scheitern alle. Sie können ...

SFINAE?

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

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

Schreiben Sie eine Klasse, die eine l-oder-rvalue-Referenz verwendet?

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 

Das Beste, was Sie tun können, ist eine Weiterleitungsfunktion einführen mit einem Chooser:

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>{}); 
} 

Aber Sie erwähnt in a comment, dass dies auch nicht für Sie arbeiten. Ich schätze, Ihre einzige Option ist: Schreiben Sie einen Vorschlag an das Normenkomitee!


Eigentlich gibt es Hoffnung! Wenn Konzepte werden (guten Punkt, TartanLlama!) Angenommen:

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

Es ist möglich, mindestens die lvalues ​​mit 'template auto foo (T & t);') zu erhalten, aber es scheint keinen Weg zu geben, dies für rvalues ​​zu tun. Es sei denn, Sie verwenden Konzepte, das ist (wo Sie eine definieren können reiner abgeleiteter rvalue-Referenzparameter), aber dann können Sie die einfachere Lösung verwenden, um beide über eine Constrained-Weiterleitung ref zu erhalten. – dyp