Template- und Copy-Constructor

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
anonym
Beiträge: 79
Registriert: 15.07.2009, 07:35
Kontaktdaten:

Template- und Copy-Constructor

Beitrag von anonym »

Ich habe ein kleines Problem mit RValue-Referenzen:

Code: Alles auswählen

class X {
public:
  template<class T> X(T&&);
  X(const X&);
};

X x(42); // aufruf von X::X(T&&)
X y(x);  // aufruf von X::X(T&&) anstatt X::X(const X&)
Wie bekomme ich es hin, dass der Copy-Constructor nicht vom Template-Constructor "verdeckt" wird?
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Template- und Copy-Constructor

Beitrag von Krishty »

Interessanter Fall … das Problem bei der zweiten Übergabe ist, dass der Template-Parameter zu X & evaluiert, womit der Konstruktor X & && (RValue-Referenz auf die temporäre Referenz zu X, die der Compiler für die Übergabe anlegt) als Parameter annimmt. Oder: Wenn du als Parameter x übergibst, hat dieser Parameter den Typen x &, und weil diese Referenz temporär und namenlos ist, liegt x & && näher als x const &.

Wenn du x mit const deklarierst, fällt diese Möglichkeit für den Compiler aus. Ich glaube nicht, dass es sauberer geht, weil ein template-Konstruktor de-facto (auch ohne RValues) die komplette C++-Typsicherheit aushebelt (und zwar ganz ganz ganz besonders, wenn er nicht explicit ist!). Lös das lieber durch Überladung.

Schon meine dritte fiese Falle mit RValue-Referenzen in drei Tagen … das ist wirklich Hassliebe.

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
anonym
Beiträge: 79
Registriert: 15.07.2009, 07:35
Kontaktdaten:

Re: Template- und Copy-Constructor

Beitrag von anonym »

Ich möchte eine an boost::any angelehnte, allerdings etwas zeitgemäßere Variant-Klasse mit Move-Semantics und eventuell, je nachdem was der Profiler sagt, einem "Small-String-Optimization"-Äquivalent dafür hinzufügen. Das Ziel dabei ist, dass ich gewisse Typen direkt als atomare Typen (alle Built-In, Vector, Matrix, usw.) unterstützen möchte und zusätzlich andere, die Reflection unterstützen (von Reflectable-Basisklasse abgeleitet), um die Variantklasse für in meinem Reflectionsystem zu verwenden. Hierfür habe ich in die Template-Metaprogrammierung-Trickkiste gegriffen, um festzustellen, ob eine Klasse Reflectable ist. Sonst müsste ich alle Typen einzeln dem Variant bekannt machen. Wenn ein Typ weder "atomar" noch Reflectable ist, meldet sich deshalb ein static_assert und außerdem noch der Compiler selbst. Wo ich schon einmal dabei war, habe ich dynamic_cast durch das System ersetzt, sodass zur Laufzeit Casts nur noch als Integer-Vergleich möglich sind, anstatt C++-RTTI zu verwenden.

Code: Alles auswählen

template<class U> struct D				
{ 
private:
	class IsRefl { char c[2]; }; 
	class IsNotRefl { char c[3]; }; 
	static IsRefl checkRefl(Reflectable*);
	static IsNotRefl checkRefl(...);
public:
	enum { v = ((sizeof(IsRefl) == sizeof(checkRefl(static_cast<Reflectable*>(static_cast<U*>(nullptr))))) ? Flags::Refl : 0), p = 0 }; 
	static_assert(v != 0, "Occurence of type not supported by type system."); 
};

template<>			struct D<Bool>			{ enum { v = Flags::Bool,   p = 0 }; };
template<>			struct D<Char>			{ enum { v = Flags::Char,   p = 0 }; };
template<>			struct D<Int1>			{ enum { v = Flags::Int1,   p = 0 }; };
template<>			struct D<Int2>			{ enum { v = Flags::Int2,   p = 0 }; };
template<>			struct D<Int4>			{ enum { v = Flags::Int4,   p = 0 }; };
template<>			struct D<Int8>			{ enum { v = Flags::Int8,   p = 0 }; };
template<>			struct D<UInt1>			{ enum { v = Flags::UInt1,  p = 0 }; };
template<>			struct D<UInt2>			{ enum { v = Flags::UInt2,  p = 0 }; };
template<>			struct D<UInt4>			{ enum { v = Flags::UInt4,  p = 0 }; };
template<>			struct D<UInt8>			{ enum { v = Flags::UInt8,  p = 0 }; };
template<>			struct D<Float4>		{ enum { v = Flags::Float4, p = 0 }; };
template<>			struct D<Float8>		{ enum { v = Flags::Float8, p = 0 }; };
template<>			struct D<String>		{ enum { v = Flags::String, p = 0 }; };

template<class U>	struct D<U&>			{ enum { v = Flags::Ref | D<U>::v, p = D<U>::p }; };
template<class U>	struct D<U*>			{ enum { v = (1 << Flags::PtrOffset) + D<U>::v, p = D<U>::p + 1 }; static_assert(p < 7, "More than 6 pointer indirections not supported"); };

template<class U>	struct D<const U>		{ enum { v = 1 << (Flags::ConstOffset + D<U>::p) | D<U>::v, p = D<U>::p }; };


// BEISPIEL
Variant v(42.0);
v.as<String>(); // knallt
Float8 = v.as<Float8>(); // geht
Sind da irgendwelche Fehler drinnen? Soweit ich das bisher getestet habe, funktioniert es. Jeder Reflectable-Type wird als nächsten Schritt eine ID bekommen, die dann auch in diesem Fall korrektes Casten ermöglicht.
Zurück zu dem ursprünglichen Problem: Gäbe es sonst noch ähnlich einfache alternative Variant-Lösungen, oder muss ich auf Move-Semantics verzichten? Oder das boost::variant-Monster verwenden? Wollte eigentlich so viel wie möglich selber bauen.
DBGTMaster
Beiträge: 14
Registriert: 31.01.2004, 10:23

Re: Template- und Copy-Constructor

Beitrag von DBGTMaster »

Mal ne frage ganz am Rande :-) ...

Was genau bedeuten die Doppel "&" Zeichen, also X&& ??
Benutzeravatar
Krishty
Establishment
Beiträge: 8268
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: Template- und Copy-Constructor

Beitrag von Krishty »

C++0x’ RValue-Referenz.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten