2017-01-07 5 views
1

Gibt es Unterschiede zwischenStandardvorlage Argumente für Funktionsschablonen Syntax

template <typename ForwardIter, 
    typename Comp = std::less<typename std::iterator_traits<ForwardIter>::value_type>> 
void select_sort(ForwardIter first, ForwardIter last, Comp comp = Comp()) 
{ 
    for (; first != last; ++first) 
    { 
     auto iter = std::min_element(first, last, comp); 
     std::swap(*first, *iter); 
    } 
} 

und die einfachere Version

template <typename ForwardIter, 
    typename Comp = std::less<>> 
void select_sort(ForwardIter first, ForwardIter last, Comp comp = Comp()) 
{ 
    // Same as above 
} 

Beide zu arbeiten scheinen. Ist es nur ein Stilproblem? Oder sind die Fälle, in denen man das eine oder das andere wählen muss?

Antwort

2

Beide funktionieren, aber sie sind unterschiedlich.

std::less<T> ist der am häufigsten verwendete Fall. Es ist eine Klasse, die die operator() überlastet haben. Es ist in ähnlicher Weise implementiert.

Ich habe einige Sonderfälle über Zeiger hier weggelassen, aber es ist nur eine naive Implementierung der Einfachheit halber.

template<typename T> 
struct less { 
    constexpr bool operator()(const T &lhs, const T &rhs) const { 
     return lhs < rhs; 
    } 
}; 

Es ist wie ein Funktor, aber Sie wählen, welche T es sein wird. Der Vorteil dieser Lösung ist, dass man sich für spezielle Fälle seines Codes auf std::less<T> spezialisieren kann.

std::less<> ist anders. Es kann Objekte beliebiger Typen vergleichen, solange die beiden Typen operator< überlastet sind. Es verhält sich genau wie ein generisches Lambda. Es ist ein bisschen wie folgt umgesetzt:

template<> 
struct less<void> { 
    template<typename T, typename U> 
    constexpr auto operator()(T&& lhs, U&& rhs) const 
      -> decltype(std::declval<T>() < std::declval<U>()) { 
     return std::forward<T>(lhs) < std::forward<U>(rhs); 
    } 
}; 

Wie Sie sehen können, wenn Sie std::less<>{}(a, b) verwenden, ist es dasselbe ist ganz in der Nähe ist, als ob Sie a < b geschrieben, und funktioniert auch mit Typen, die eine nicht-const hat operator<. Also dieser ist der beste, solange die Klassen, die Sie verwenden, die operator< überladen haben.

+0

Bekam es. Vielen Dank. – user515430

+1

Der Vollständigkeit halber: die spezielle Ausnahme, daß 'std :: less ()' auch für Zeiger erlaubt ist, die nicht auf das gleiche Array zeigen (anders als '<'), erstreckt sich auch auf 'std :: less <>()' . Die Definition, die Sie in diese Antwort eingeben, würde das nicht richtig behandeln.(Ich weiß, dass Sie "ein bisschen wie" geschrieben haben.) – hvd

+0

@hvd Ich korrigierte meine Antwort, danke für das Aufzeigen! –

2

typename Comp = std::less<typename std::iterator_traits<ForwardIter>::value_type> bedeutet, dass Comp nicht angegeben ist, wird std::less<typename std::iterator_traits<ForwardIter>::value_type> verwendet.

typename Comp = std::less<> bedeutet, dass Comp nicht angegeben ist, wird std::less<> verwendet.

Das ist der genaue Unterschied. Es bedeutet nicht, dass der Compiler das Template-Argument für std::less<> irgendwie ableitet. Da die Vorlage std::less<T=void> einen Standardwert angibt, wird stattdessen dieser Standardwert verwendet.

Die Template-Spezialisierung std::less<void> ist so definiert, dass sie in der Regel anstelle von anderen std::less<T> Versionen verwendet werden kann. Sie sind nicht genau dasselbe, es gibt Fälle, in denen du das eine oder das andere benutzen würdest. Von Ihrem Fragetitel scheint jedoch Ihre Frage über die Syntax des Standardarguments zu sein, nicht speziell über std::less, also werde ich die Erklärung davon überspringen.

+0

Soweit ich sagen kann, ist das erste Teilstück nicht gültig bis C++ 14 –

+0

@ EdgarRokyan Welches ist die aktuelle Version von C++, so vorausgesetzt, wenn nicht anders angegeben. (Ich denke du meinst übrigens "zweites Stück".) – hvd

+0

Ja, definitiv. Scheint, dass ich eine Konzentration bis zum Ende des Tages verloren habe :) –

Verwandte Themen