2016-04-01 4 views
0

Die folgende kompiliert fein:Templated Konstruktor passend besser als Templat-Typ-Konvertierung

#include <iostream> 
#include <vector> 

class Point : public std::vector<double> 
{ 
public: 
    Point() = default; 
}; 

class MyClass 
{ 
public: 

    template <typename T> 
    operator T() const { return T(); } 
}; 

int main() 
{ 
    MyClass myClass; 
    Point test = myClass; 

    return 0; 
} 

Allerdings, wenn ich einen Templat-Konstruktor haben, ist es nicht:

#include <iostream> 
#include <vector> 

class Point : public std::vector<double> 
{ 
public: 
    Point() = default; 

    template <typename TVector> 
    Point(const TVector& v) 
    { 
     (*this)[0] = v[0]; // compiler error on this line (saying no operator[] for MyClass, but the point is that this function is used instead of the type conversion function) 
    } 

}; 

class MyClass 
{ 
public: 

    template <typename T> 
    operator T() const { return T(); } 
}; 

int main() 
{ 
    MyClass myClass; 
    Point test = myClass; 

    return 0; 
} 

Ich kann die Point Klasse ändern (um ein zweites Standardkonstruktorargument oder ähnliches hinzuzufügen), gibt es also eine Möglichkeit, nur MyClass zu ändern, damit dies funktioniert?

+0

Welchen Compiler benutzen Sie? Mit gcc und clang erhalte ich eine mehrdeutige Suche. –

+0

@RyanHaining g ++ 4.8.4 –

+1

Vorlagen für Templates müssen Typen genau entsprechen und Typkonvertierungen nicht zulassen. Wenn Sie 'Point' nicht ändern können, können Sie unglücklicherweise' MyClass' nicht verwenden. – SergeyA

Antwort

1

können Sie SFINAE verwenden Sie den Konstruktor zu deaktivieren, wenn es in der TVector

template <typename TVector, typename=decltype(std::declval<const TVector&>()[0])> 
Point(const TVector& v) 

keine operator[] ist Beim Versuch, die zweite typename der Compiler ableiten, was TVector::operator[] kehrt müssen zu bewerten. Wenn dies nicht möglich ist, wird der Konstruktor aus der Überladungsauflösung geworfen.

Wenn Sie es ein bisschen erarbeiten möchten Sie enable_if verwenden können, um ein bisschen mehr explizit in aussehen zu drehen Point

template <typename...> 
using void_t = void; 

template <typename T, typename = void> 
struct HasIndexOperator : std::false_type {}; 

template <typename T> 
struct HasIndexOperator<T, 
      void_t<decltype(std::declval<const T&>()[0])>> : std::true_type {}; 

dann Konstruktor wird:

template <typename TVector, 
      typename=std::enable_if_t<HasIndexOperator<TVector>{}>> 
     Point(const TVector& v) 
+0

Bitte erläutern Sie das Verhalten, das OP gerade sieht. Danke –

+0

@RyanHaining, Ihr Vorschlag ist korrekt, obwohl Sie nicht sagen, warum :) Es wäre nur Kopie Konstruktor zur Verfügung, die nicht-templated wäre. – SergeyA

+0

@SergeyA 'wirft den Konstruktor weg 'ist der Grund, warum es eindeutig ist. Ich nehme an, OP weiß, dass es einen Kopierkonstruktor gibt, der bereits –

-1

Template Konstruktor wie dieses

würde als benutzerdefinierte const funktionieren ructor und akzeptiert alles, was nicht vom Typ Point ist (das würde immer noch von einem implizit definierten Kopierkonstruktor gehandhabt werden).

mit

Point point = myclass; 

Compiler findet Templat-Konstruktor führt keine Umbauten und versucht operator[] auf der MyClass zu nennen.

Wenn Sie Konstruktor deaktivieren Templat (als Ryan schon sagt) für Nicht-Vektoren, das einzige, was übrig bleibt, ist eine nicht-Vorlage Copykonstruktor, implizit deklariert als

Point(const Point&); 

Compiler Konvertierung ausführen soll von MyClass zu Point und alles wird kompiliert.

+0

'sowohl Kopiekonstruktor als auch benutzerdefinierter Konstruktor 'ist, das ist nicht korrekt. Der Compiler sieht dies als Normalwertkonstruktor und generiert immer noch einen Kopierkonstruktor –

+0

@RyanHaining, yep, Sie sind richtig. – SergeyA

+0

@SergeyA Ich denke ich bin verwirrt darüber, warum der Kopierkonstruktor hier involviert ist? Ist nicht 'Point test = myClass;' mit dem Konvertierungsoperator gefolgt vom Zuweisungsoperator? –