2016-08-20 2 views
5

Ich habe eine Funktion Vorlage, die eine std::pair sowie einen Wert von einem der Typen des Paares dauert. Ich möchte diese Funktion mit einem Eintrag von std::map als Paar-Argument aufrufen.Wie const/nicht-Const Konflikt in Vorlage Argument Abzug

#include <map> 
#include <utility> 

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
    // Imagine that this does something important... 
} 

int main() { 
    std::map<int, float> foo { { 0, 0.0 } }; 

    do_stuff(*foo.begin(), 0); 
} 

Dies nicht kompilieren, weil die Art der Eintragung der Karte std::pair<const int, float> ist, so dass der Typ Abzug für T1 hat widersprüchliche Typen: const int über das pair Argument und int über das val Argument.

test.cc: In function ‘int main()’: 
test.cc:12:27: error: no matching function for call to ‘do_stuff(std::pair<const int, float>&, int)’ 
    do_stuff(*foo.begin(), 0); 
         ^
test.cc:5:6: note: candidate: template<class T1, class T2> void do_stuff(const std::pair<_T1, _T2>&, const T1&) 
void do_stuff(std::pair<T1, T2> const &pair, T1 const &val) { 
     ^~~~~~~~ 
test.cc:5:6: note: template argument deduction/substitution failed: 
test.cc:12:27: note: deduced conflicting types for parameter ‘const T1’ (‘const int’ and ‘int’) 
    do_stuff(*foo.begin(), 0); 
         ^

Was ist der beste Weg, um diesen Konflikt zu lösen? Idealerweise würde ich gerne T1 als int herleiten, aber es ist in Ordnung, wenn es const int ist, wenn das einfacher zu implementieren ist.

Ich habe festgestellt, dass ich den Fehler, indem Sie entweder std::remove_const oder std::decay von der Art des val Parameter auflösen kann:

void do_stuff(std::pair<T1, T2> const &pair, typename std::remove_const<T1>::type const &val) { 

aber ich weiß nicht, von denen, die besser geeignet ist, oder wenn Es gibt eine andere Lösung, die besser wäre.

+0

nicht sicher zu verstehen ... warum definieren Sie nicht 'do_stuff()' entsprechend; Ich meine 'Vorlage void do_stuff (Std :: Paar const & Paar, T1 const & val)'? – max66

+0

@ max66, ich will es auch mit Paaren arbeiten, deren erster Typ * nicht * const ist. (Da der ganze Parameter von const referenz übergeben wird, ist die constance der Mitglieder des Paars irrelevant; entweder const oder non-const sollte für beide erlaubt sein.) – Wyzard

+0

Haben Sie versucht, in der Karte eine Konstruktion zu erstellen? Ich meine 'std :: map foo {{0, 0.0}};' – mtb

Antwort

5

Eine Lösung ist std::add_const statt das Schlüsselwort const direkt zu verwenden.

Die Rundfahrt über eine Vorlage verhindert über diesen Parameter Typ Typ Abzug:

#include <map> 
#include <type_traits> 
#include <utility> 

template< class T1, class T2 > 
void do_stuff(std::pair<T1, T2> const& pair, std::add_const_t<T1>& val) 
{ 
    // Imagine that this does something important... 
    (void) pair; (void) val; 
} 

auto main() 
    -> int 
{ 
    std::map<int, float> foo { { 0, 0.0f } }; 
    do_stuff(*foo.begin(), 0); 
} 
+4

"' auto main() -> int' "Sie haben das zu weit genommen – Dani

+1

@Dani meinst du das' int' ist ein Zeilenumbruch zu weit vom Funktionsnamen in seinem Code entfernt? –

+0

@ Cheersandhth.-Alf So ist 'auto main' jetzt in C++ 17 erlaubt? (http://stackoverflow.com/questions/17134975/will-automatic-return-type-deduction-work-for-main) – kfsone

2

Nicht wirklich elegant, aber ich nehme an, Sie T1 in zwei verschiedenen Vorlage paramenters trennen konnte.

So etwas wie

template <typename T1, typename T2, typename T3> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const &val) { 
    // Imagine that this does something important... 
} 

Sie einen Scheck hinzufügen, über std::enable_if_t, eine Korrelation zwischen T1 und T3 zu verhängen; von Beispiel

template <typename T1, typename T2, typename T3, 
     typename = std::enable_if_t<std::is_same<std::remove_const_t<T1>, std::remove_const_t<T3>>::value>> 
void do_stuff(std::pair<T1, T2> const &pair, T3 const & val) { 
    // Imagine that this does something important... 
} 
+0

Das löst den Kompilierungsfehler, aber ermöglicht dem Aufrufer außerdem, einen vollständig anderen, nicht verwandten Typ zu übergeben. (Es wird immer noch einen Kompilierfehler erzeugen, wenn 'T3' ​​nicht mit' T1' kompatibel ist, aber der Fehler kommt von irgendwo innerhalb der Implementierung der Funktion, wo es weniger wahrscheinlich ist, was das eigentliche Problem ist.) – Wyzard

+0

@Wyzard - Sie können über SFINAE festlegen, dass "T1" und "T3" ("const" mehr oder weniger) gleich sind; hässlicher als vorher, aber ... Modifiziert meine Antwort entsprechend. – max66

+0

In 'typename = typenname std :: enable_if_t' wird der zweite' typename' nicht benötigt, das ist der Punkt des '_t' Alias. – Oktalist

3
template<class T>struct identity{using type=T;}; 
template<class T>using no_deduce=typename identity<T>::type; 

Wrap der zweite Typ in no_deduce Abzug zu blockieren.

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, no_deduce<T1> const &val) { 
    // Imagine that this does something important... 
} 

Das funktioniert und es ist klar, warum Sie es tun.

Jetzt könnte es sich lohnen, darüber nachzudenken, was Sie tun möchten, wenn T1 ein Referenztyp ist und was const& in diesem Fall tut. Stellen Sie sich T1 ist int& dann int& const& wird nur ein int&.

Dies ist möglicherweise nicht was Sie wollen.

Vielleicht, was Sie wollen, was Sie wirklich, wirklich wollen, ist:

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, std::remove_reference_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

Wenn Sie ein const& wollen Sie vergessen, muss Ihr &, wenn Sie verhindern wollen val aus geändert werden Sie besser sicherstellen, dass es const ist . Jetzt verschwenden Sie nicht Ihre wertvollen const&, remove_reference_t und es wird gut.

Wenn Sie mit volatile umgehen wollen, müssen Sie mit remove_volatile_t bekommen. Binden Sie sie für immer zusammen in einer template<class T>using clean_t=std::remove_cv_t<remove_reference_t<T>>. Wenn Sie const& wollen haben Sie zu

template <typename T1, typename T2> 
void do_stuff(std::pair<T1, T2> const &pair, clean_t<T1> const &val) { 
    // Imagine that this does something important... 
} 

Einfach const& zu einfach ist, aber das ist so, wie es ist.

Verwandte Themen