2015-10-01 6 views
5

den folgenden Ausschnitt Angenommen:Geben nullptr einen Typ für Vorlage Abzug

template <class T> 
void fct(T* a, T* b){ 
    // do something 
} 

A a; 
fct(&a, nullptr); // Problem here! 

Diese Mühe macht, da die Aufrufargumente vom Typ sind A* und nullptr_t und so kann der Compiler nicht Template-Parameter T ableiten.

Im Allgemeinen kann ich mehrere Ideen vorstellen, wie dieses Problem zu lösen:

  • A* b = nullptr definieren und verwenden fct(&a, b)
  • Definieren Sie eine Überlastung mit einem Argument für fct für den nullptr Fall
  • Verwenden fct(&a, static_cast<A*>(nullptr))

Oder gibt es eine sauberere Lösung, li ke die Schaffung eines so etwas wie ein "typisiertes nullptr"?

+0

Wie könnte Ihr getippter "nullptr" von "static_cast (nullptr)" abweichen? – Petr

+2

Ihre zweite Alternative sieht wie die beste Wette aus ... Überladung (Template-Spezialisierung) – basav

+0

Sie bereits in Frage beantwortet ... 1. und 3. Alternativen sind mehr oder weniger die gleichen: Sie brauchen ein T * und nullptr ist nicht so Sie verbieten ' fct (& a, nullptr) ', und 2. verwendet eine explizite Überladung, um es zuzulassen. Ich kann mir keine andere Antwort vorstellen ... –

Antwort

2

Ich würde auch die folgende Lösung vorschlagen:

template <class T, class U> 
void fct(T* a, U b){ 
    T* b2 = b; 
    // do something 
} 

A a; 
fct(&a, nullptr); 

Dies ermöglicht eine breitere Nutzung von fct, aber vielleicht ist das genau das, was Sie wollen.

Betrachten wir zum Beispiel

class A {}; 
class B : public A {}; 

... 
A a; 
B b; 
fct(&a, &b); // calls fct<A> 
// the following will not compile: 
// fct(&b, &a); 
1

Sie den folgenden Code verwenden:

#include <type_traits> 

template<class T> 
void f_impl(T*, T*) 
{ 
    std::cout << typeid(T).name() << "\n"; 
} 


template<class T, class U> 
void f(T l, U r) 
{ 
    static_assert((std::is_same<T, U>::value && std::is_pointer<T>::value) || 
        (std::is_same<T, std::nullptr_t>::value && std::is_pointer<U>::value) || // First non-null 
        (std::is_same<U, std::nullptr_t>::value && std::is_pointer<T>::value) // Second non-null 
        , ""); 

    using P = typename std::conditional<std::is_same<T, std::nullptr_t>::value, U, T>::type; 

    f_impl<typename std::remove_pointer<P>::type>(l, r); 
} 

int main() 
{ 
    int i; 
    f(&i, nullptr); 
    f(nullptr, &i); 
    // f(i, nullptr); // won't compile - non-pointer 
    f(&i, &i); 

    double d; 
    // f(&i, &d); // - won't compile 

} 

Diese Version Tests f mit einem nullptr (aber nicht beide) oder mit zwei Zeigern nennen können zum selben Typ. Mit C++ 14 können Sie auch Dinge wie std::conditional_t, std::remove_pointer_t und std::is_null_pointer verwenden, um einige Bio-Platten zu entfernen.

+0

Sie müssen 'f ' oder 'f ' aufrufen, je nachdem, welches nicht null ist; Ihre aktuelle Version ruft 'f ' auf, wenn sie als 'f (nullptr, & i)' aufgerufen wird. – Petr

+0

@Petr Es gab mehr Probleme mit diesem Code. Ich habe es repariert. Vielen Dank! – Rostislav

5

Genau das zweite Argument, ein nicht-abgeleitete Kontext, z:

template <class T> 
void fct(T* a, std::remove_reference<T*>::type b) { 
+0

fehlt 'typename' vor dem zweiten Funktionsargument? Man könnte auch das neuere und etwas weniger ausführliche 'std :: remove_reference_t' verwenden. – user1735003

0

Da die Frage schon sagt, nullptr hat in der Tat eine Art: std::nullptr_t. So fügen Sie einfach eine explizite Überlastung für speziell diesem Fall:

template <class T> 
void fct(T* a, std::nullptr_t b) { return fct<T>(a,static_cast<T*>(b)); } 

Keine Notwendigkeit, einige Template-Argument haben class U dafür.