initializer_list und static_assert
-
template<typename T, size_t N> class Vector { private: T data[N]; public: Vector(initializer_list<T> const& init) { //static_assert(N == init.size(), ""); } }Das static_assert ist so nicht möglich, da init keine constant expression ist. Gibt es Tricks, mit denen eine statische Prüfung der Größe doch irgendwie möglich ist?
Bei std::array funktioniert es ja auch, allerdings hat ein Blick in den Header nicht weitergeholfen. Ist dort evtl. Compilermagie am Werk?
-
Hatten wir gerade erst http://www.c-plusplus.net/forum/319580
-
Gibt es Tricks, mit denen eine statische Prüfung der Größe doch irgendwie möglich ist?
Nein. Aber da du hier eine feste Anzahl an Parametern willst, bietet sich ein variadic template an.
template<typename T, size_t N> class Vector { private: T data[N]; public: template<typename ... Types> Vector( Types&&... args ) : data{ std::forward<Types>(args)... } { static_assert( sizeof...(Types) <= N, ""); } };Funktioniert* dank §8.5.1/7.
~* (Ungetestet!)~
-
Na damit geht's doch, warum sagst Du dann "Nein"

Btw: sizeof...(Types)
-
Nein, geht nicht mit C++11. GCC 4.8 bspw. deklariert alle Funktionen bereits
constexpr, was aber nicht standardkonform ist. (s. §18.9/1)Edit:
Btw: sizeof...(Types)
Danke. Ein Flüchtigkeitsfehler...

-
Dummerweise geht damit folgendes nicht mehr:
Vector<float, 4> v(1.0f, 2.0f, 0.0f, 1.0f);
Vector<float, 4> w = v;"cannot convert 'Vector<float, 4u>' to 'float' in initialization|"
Ein zustätzlicher Coopy-Constructor wird auch nicht aufgerufen.
Ich glaube ich verzichte doch darauf...
-
wie wär's mit
template<bool...B> struct and_ : std::true_type {}; template<bool...B> struct and_<false,B...> : std::false_type {}; template<bool...B> struct and_<true,B...> : and_<B...> {}; template<class T, size_t N> class Vector { private: T data[N]; public: template<class...Types ,class = typename std::enable_if< (sizeof...(Types)==N) && and_<std::is_convertible<Types,T>::value...>::value >::type > explicit Vector(Types&&...args) : data{std::forward<Types>(args)...} {} };stattdessen? (auch ungetestet)
-
Freddy Kruger schrieb:
Dummerweise geht damit folgendes nicht mehr:
Vector<float, 4> v(1.0f, 2.0f, 0.0f, 1.0f);
Vector<float, 4> w = v;"cannot convert 'Vector<float, 4u>' to 'float' in initialization|"
Das liegt übrigens daran, dass der variadische Konstruktor bei der Überladungsauflösung gegenüber dem Kopierkonstruktor "gewinnt", weil beim Kopierkonstruktor ja noch eine "const-Konvertierung" für v dabei ist.
-
krümelkacker schrieb:
Das liegt übrigens daran, dass der variadische Konstruktor bei der Überladungsauflösung gegenüber dem Kopierkonstruktor "gewinnt", weil beim Kopierkonstruktor ja noch eine "const-Konvertierung" für v dabei ist.
Völliger Blödsinn. Der Copy-Konstruktor wird bevorzugt, weil er kein function-parameter-pack hat. Dein Compiler, Freddy Krueger, ist einfach noch nicht aktuell genug. Siehe hier: GCC 4.8.1 kann es schon richtig: http://ideone.com/ewmqpP
13.3.3.2/2 schrieb:
a standard conversion sequence is a better conversion sequence than [...] an ellipsis conversion sequence
Und überhaupt, reference-compatible with added qualification ist eine identity conversion.
-
Sone schrieb:
13.3.3.2/2 schrieb:
a standard conversion sequence is a better conversion sequence than [...] an ellipsis conversion sequence
Ich sehe hier nirgens eine "ellipsis conversion" sondern nur einen Pack-Expansion Operator. Dein Zitat ist also irrelevant.
Sone schrieb:
Und überhaupt, reference-compatible with added qualification ist eine identity conversion.
Ja und? Du sagst das so, als ob das hier entweder zu einem Fehler (wegen Ambiguität) führen müsste, weil beides eine "identity conversion" ist, oder die normale foo-Funktion dran kommen müsste, weil sie kein Template ist. Offensichtlich ist dem nicht so. Und das, was dort passiert, ist dasselbe Problem in grün.
Das, was von deiner Erklärung übrig bleibt, ist die Sache mit der "Variadität" des Konstruktors. Aber die allein verhindert auch nicht, dass der Konstruktor zum Kopieren verwendet wird. Du hast nämlich gemogelt. Du hast den Konstruktor
explicitgemacht. Das ist der einzige Grund, warum Dein letztes Beispiel mit der Kopierinitialisierung kompiliert. Entfernst du dasexplicitoder machst du aus der Kopierinitialisierung eine direkte, dann wird zum Konstruktor-Template aufgelöst, was dann einen Fehler produziert...Fazit: Dein Beitrag war also
Sone schrieb:
Völliger Blödsinn.

Nächstes mal bitte die Klappe nicht soweit aufreißen. Danke.
-
Du hast nämlich gemogelt. Du hast den Konstruktor explicit gemacht. Das ist der einzige Grund, warum Dein letztes Beispiel mit der Kopierinitialisierung kompiliert. Entfernst du das explicit oder machst du aus der Kopierinitialisierung eine direkte, dann wird zum Konstruktor-Template aufgelöst, was dann einen Fehler produziert...
Ahh.... da hast du völlig Recht. Wie konnt' ich das nur übersehen.
Ja und? Du sagst das so, als ob das hier entweder zu einem Fehler (wegen Ambiguität) führen müsste, weil beides eine "identity conversion" ist, oder die normale foo-Funktion dran kommen müsste, weil sie kein Template ist. Offensichtlich ist dem nicht so. Und das, was dort passiert, ist dasselbe Problem in grün.
Das habe ich nie gesagt. Überhaupt, das hier wollte ich auch posten:
Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if [...] S1 and S2 are reference bindings (8.5.3), and the types to which the references refer are the same type except for top-level cv-qualifiers, and the type to which the reference initialized by S2 refers is more cv-qualified than the type to which the reference initialized by S1 refers.
Ja, mein Missverstädnis lag wohl darin, dass ich immer dachte, mit dem Zitat
An ellipsis conversion sequence occurs when an argument in a function call is matched with the ellipsis parameter specification of the function called
Bezog sich der Standard auf alle Funktionen, die eine variable Anzahl von zuschüssigen Parametern erlauben - ob durch eine variadic function (wie bei C) oder ein parameter-pack.
Nächstes mal bitte die Klappe nicht soweit aufreißen. Danke.
Du weißt, ich mag das :p
Das passiert so einmal im Monat, dann halte ich die nächste Weile die Klappe.
-
#include <type_traits> #include <cstddef> #include <utility> template<bool...B> struct and_ : std::is_same<and_<true, B...>, and_<B..., true>> {}; template<class T, size_t N> class Vector { private: T data[N]; public: template<class...Types , typename std::enable_if< (sizeof...(Types)==N) && and_<std::is_convertible<Types,T>::value...>::value, int >::type = 0 > Vector(Types&&...args) : data{std::forward<Types>(args)...} {} }; Vector<float, 4> foo() { return {1.0f, 2.0f, 0.0f, 1.0f}; }Ist der Konstruktor explicit, kann foo nicht mehr so geschrieben werden. enable_if in einem non-type-Templateparameter zu verwenden, ist vorteilhaft, wenn mehrere Templates überladen werden sollen (man benötigt nicht zusätzliche Dummy-Parameter: der Typ des Templateparameters ist Teil der Signatur des Templates, ein Defaultargument dagegen nicht), und andernfalls genausogut wie in krümelkackers Version.