Standardvorlagenargumente für Funktionsvorlagen

187

Warum sind Standardvorlagenargumente nur für Klassenvorlagen zulässig? Warum können wir keinen Standardtyp in einer Elementfunktionsvorlage definieren? Beispielsweise:

struct mycclass {
  template<class T=int>
  void mymember(T* vec) {
    // ...
  }
};

Stattdessen erzwingt C ++, dass Standardvorlagenargumente nur für eine Klassenvorlage zulässig sind.

Arman
quelle
8
+1 Das ist wirklich eine schwierige Frage.
AraK
1
Betrachten Sie für die ersten drei veröffentlichten Antworten das folgende Beispiel: struct S { template <class R = int> R get_me_R() { return R(); } };Der Vorlagenparameter kann nicht aus dem Kontext abgeleitet werden.
AraK
3
Gute Frage. 3 Leute haben bereits geantwortet, dass es "keinen Sinn ergibt", und sie sind alle im Allgemeinen falsch. Funktionsvorlagenparameter können nicht immer von den Funktionsaufrufparametern abgezogen werden. Wenn sie zum Beispiel erlaubt wären, könnte ich schreiben template <int N = 1> int &increment(int &i) { i += N; return i; }und dann increment(i);oder increment<2>(i);. So wie es ist, muss ich schreiben increment<1>(i);.
Steve Jessop
Tatsächlich können meine und AraKs Beispiele beide durch Überladung behandelt werden. Ich denke, Litb's können nicht, weil der Template-Parameter möglicherweise abgeleitet oder angegeben wird.
Steve Jessop
3
@Steve: Das fehlende Semikolon ist eine neue Überladung von EOL-Operatoren, die B. Stavtrups "Überladung von C ++ - Leerzeichen" ergänzt und im Journal of Object-Oriented Programming vom 1. April 1992 veröffentlicht wurde. ( Www2.research.att.com/~bs/ Papers.html )

Antworten:

148

Es ist sinnvoll, Standardvorlagenargumente anzugeben. Zum Beispiel könnten Sie eine Sortierfunktion erstellen:

template<typename Iterator, 
         typename Comp = std::less<
            typename std::iterator_traits<Iterator>::value_type> >
void sort(Iterator beg, Iterator end, Comp c = Comp()) {
  ...
}

C ++ 0x führt sie in C ++ ein. Siehe diesen Fehlerbericht von Bjarne Stroustrup: Standardvorlagenargumente für Funktionsvorlagen und was er sagt

Das Verbot von Standardvorlagenargumenten für Funktionsvorlagen ist ein falsch verstandener Rest der Zeit, in der freistehende Funktionen als Bürger zweiter Klasse behandelt wurden und alle Vorlagenargumente aus den Funktionsargumenten abgeleitet und nicht angegeben werden mussten.

Die Einschränkung schränkt den Programmierstil erheblich ein, indem freistehende Funktionen unnötig von Elementfunktionen unterschieden werden, wodurch das Schreiben von Code im STL-Stil erschwert wird.

Johannes Schaub - litb
quelle
@Arman, der Link zum Fehlerbericht enthält die Änderungen, die am Arbeitsentwurf für C ++ 0x und an den Diskussionen vorgenommen wurden. Argumente, die weder abgeleitet noch explizit angegeben wurden, werden aus Standardargumenten erhalten. GCC4.4 unterstützt Standardargumente für Funktionsvorlagen im C ++ 0x-Modus.
Johannes Schaub - litb
4
Nichts mit der Frage oder Antwort zu tun, aber Herb Sutter hat nach dem letzten Samstagstreffen den kommenden Standard C ++ 11 aufgerufen. Ich habe es heute gerade gelesen und möchte es teilen :) herbsutter.wordpress.com/2010/03/13/…
David Rodríguez - dribeas
und die obligatorische Folgefrage ... wann wird dies voraussichtlich in andere Compiler gelangen :)
Jamie Cook
@ JohannesSchaub-litb Ich hatte das gleiche Problem: Keine Möglichkeit, den Standardtyp in einer Vorlagenfunktion anzugeben. Ich habe durch eine explizite Instanziierung der Funktion auf den Standardtyp ( doublein meinem Fall) gelöst . Vielleicht ist es nicht "allgemein", aber gibt es einen Nachteil bei dieser Praxis? Vielen Dank.
JackOLantern
Der folgende Code kompiliert nicht mit Fehlern wie error: invalid conversion from ‘int’ to ‘int*’einer Idee, warum: `#include <array> #include <algorithm> #include <functional> template <Typname Iterator, Typname Comp = std :: less <Iterator>> void my_sort ( Iterator betteln, Iteratorende, Comp c = Comp ()) {std :: sort (betteln, enden, c); } int main () {std :: array <int, 5> ar {5,2,21,7,4}; my_sort (ar.begin (), ar.end ()); } `
Luke Peterson
36

So zitieren Sie C ++ - Vorlagen: Das vollständige Handbuch (Seite 207):

Wenn Vorlagen ursprünglich zur C ++ - Sprache hinzugefügt wurden, waren explizite Funktionsvorlagenargumente kein gültiges Konstrukt. Funktionsvorlagenargumente mussten immer aus dem Aufrufausdruck ableitbar sein. Infolgedessen schien es keinen zwingenden Grund zu geben, Argumente für Standardfunktionsvorlagen zuzulassen, da der Standard immer durch den abgeleiteten Wert überschrieben würde.

James McNellis
quelle
einfach und prägnant :)
InQusitive
17

Bisher können alle angebotenen Beispiele für Standardvorlagenparameter für Funktionsvorlagen mit Überladungen erstellt werden.

AraK:

struct S { 
    template <class R = int> R get_me_R() { return R(); } 
};

könnte sein:

struct S {
    template <class R> R get_me_R() { return R(); } 
    int get_me_R() { return int(); }
};

Mein eigenes:

template <int N = 1> int &increment(int &i) { i += N; return i; }

könnte sein:

template <int N> int &increment(int &i) { i += N; return i; }
int &increment(int &i) { return increment<1>(i); }

litb:

template<typename Iterator, typename Comp = std::less<Iterator> >
void sort(Iterator beg, Iterator end, Comp c = Comp())

könnte sein:

template<typename Iterator>
void sort(Iterator beg, Iterator end, std::less<Iterator> c = std::less<Iterator>())

template<typename Iterator, typename Comp >
void sort(Iterator beg, Iterator end, Comp c = Comp())

Stroustrup:

template <class T, class U = double>
void f(T t = 0, U u = 0);

Könnte sein:

template <typename S, typename T> void f(S s = 0, T t = 0);
template <typename S> void f(S s = 0, double t = 0);

Was ich mit folgendem Code bewiesen habe:

#include <iostream>
#include <string>
#include <sstream>
#include <ctype.h>

template <typename T> T prettify(T t) { return t; }
std::string prettify(char c) { 
    std::stringstream ss;
    if (isprint((unsigned char)c)) {
        ss << "'" << c << "'";
    } else {
        ss << (int)c;
    }
    return ss.str();
}

template <typename S, typename T> void g(S s, T t){
    std::cout << "f<" << typeid(S).name() << "," << typeid(T).name()
        << ">(" << s << "," << prettify(t) << ")\n";
}


template <typename S, typename T> void f(S s = 0, T t = 0){
    g<S,T>(s,t);
}

template <typename S> void f(S s = 0, double t = 0) {
    g<S,double>(s, t);
}

int main() {
        f(1, 'c');         // f<int,char>(1,'c')
        f(1);              // f<int,double>(1,0)
//        f();               // error: T cannot be deduced
        f<int>();          // f<int,double>(0,0)
        f<int,char>();     // f<int,char>(0,0)
}

Die gedruckte Ausgabe stimmt mit den Kommentaren für jeden Aufruf von f überein, und der auskommentierte Aufruf kann nicht wie erwartet kompiliert werden.

Ich vermute also, dass Standardvorlagenparameter "nicht benötigt" werden, aber wahrscheinlich nur in dem Sinne, dass Standardfunktionsargumente "nicht benötigt werden". Wie aus dem Fehlerbericht von Stroustrup hervorgeht, war das Hinzufügen nicht abgeleiteter Parameter zu spät, als dass jemand erkennen und / oder wirklich einschätzen könnte, dass dies die Standardeinstellungen nützlich machte. Die aktuelle Situation basiert also auf einer Version von Funktionsvorlagen, die nie Standard war.

Steve Jessop
quelle
@Steve: Also lief das Ei schneller als Hühnchen? :) interessant. Vielen Dank.
Arman
1
Wahrscheinlich nur eines dieser Dinge. Der C ++ - Standardisierungsprozess läuft teilweise langsam ab, sodass die Benutzer Zeit haben, um zu erkennen, wann eine Änderung an anderer Stelle im Standard Chancen oder Schwierigkeiten schafft. Die Leute, die den Standardentwurf umsetzen, werden hoffentlich Schwierigkeiten haben, wenn sie einen Widerspruch oder eine Unklarheit feststellen. Möglichkeiten, Dinge zuzulassen, die vorher nicht erlaubt waren, verlassen sich auf jemanden, der den Code schreiben möchte und bemerkt, dass er nicht länger illegal sein muss ...
Steve Jessop
2
Noch eine für Sie : template<typename T = void> int SomeFunction();. Der Template-Parameter wird hier niemals verwendet, und tatsächlich wird die Funktion niemals aufgerufen. Der einzige Ort, auf den es sich bezieht, ist ein decltypeoder sizeof. Der Name stimmt absichtlich mit dem Namen einer anderen Funktion überein, aber die Tatsache, dass es sich um eine Vorlage handelt, bedeutet, dass der Compiler die freie Funktion bevorzugt, falls vorhanden. Die beiden werden in SFINAE verwendet, um ein Standardverhalten bereitzustellen, bei dem eine Funktionsdefinition fehlt.
Tom
4

Unter Windows können Sie mit allen Versionen von Visual Studio diesen Fehler ( C4519 ) in eine Warnung konvertieren oder wie folgt deaktivieren:

#ifdef  _MSC_VER
#pragma warning(1 : 4519) // convert error C4519 to warning
// #pragma warning(disable : 4519) // disable error C4519
#endif

Weitere Details finden Sie hier .

Adi Shavit
quelle
1
Beachten Sie, dass, während diese nicht deaktiviert die Meldung „Standardvorlage Argumente nur auf einer Klassenvorlage erlaubt sind“, ist es nicht eigentlich die Prozessvorlage Instanziierung machen Verwendung der bereitgestellten Wert. Dies erfordert VS2013 (oder einen anderen Compiler, der den C ++ 11-Fehler 226 "Standardvorlagenargumente für Funktionsvorlagen" abgeschlossen hat)
puetzk
1

Was ich benutze, ist der nächste Trick:

Nehmen wir an, Sie möchten eine Funktion wie diese haben:

template <typename E, typename ARR_E = MyArray_t<E> > void doStuff(ARR_E array)
{
    E one(1);
    array.add( one );
}

Du wirst nicht erlaubt sein, aber ich mache den nächsten Weg:

template <typename T>
struct MyArray_t {
void add(T i) 
{
    // ...
}
};

template <typename E, typename ARR_E = MyArray_t<E> >
class worker {
public:
    /*static - as you wish */ ARR_E* parr_;
    void doStuff(); /* do not make this one static also, MSVC complains */
};

template <typename E, typename ARR_E>
void worker<E, ARR_E>::doStuff()
{
    E one(1);
    parr_->add( one );
}

Auf diese Weise können Sie es folgendermaßen verwenden:

MyArray_t<int> my_array;
worker<int> w;
w.parr_ = &arr;
w.doStuff();

Wie wir sehen können, muss der zweite Parameter nicht explizit eingestellt werden. Vielleicht ist es für jemanden nützlich.

alariq
quelle
Dies ist definitiv keine Antwort.
Welpe
@deadmg - kannst du erklären warum? Wir sind nicht alle C ++ - Vorlagengurus. Vielen Dank.
Kev
Dies ist eine Problemumgehung, die ziemlich ordentlich ist, aber nicht alle Fälle abdeckt, die Sie möglicherweise möchten. Wie würden Sie dies beispielsweise auf einen Konstruktor anwenden?
Tiberiu Savin
@TiberiuSavin - Wenn ich Sie richtig verstanden habe, können Sie Folgendes tun: Vorlage <Typname E, Typname ARR_E> Arbeiter <E, ARR_E> :: Arbeiter (ARR_E * parr) {parr_ = parr; }. Und dann benutze es so: worker <int> w2 (& my_array);
Alariq