Anzahl Elemente in initializer_list zum Übersetzungszeitpuntk begrenzen
-
Das sind wieder so Dinge, weshalb ich modernes C++ so gar nicht toll finde. Das Auskommentierte funktioniert natürlich wieder einmal nicht, und das andere funktioniert. Kennt jemand einen Weg das zu bewerkstelligen, ohne eine andere Signatur des Konstruktors zu bekommen?
#include <initializer_list> class Foo { int a, b; public: Foo (const std::initializer_list<const int>& list) { //static_assert (2 == list.size()); } }; int main () { static constexpr std::initializer_list<const int> list = {1, 2}; static_assert (2 == list.size()); Foo f = list; }
-
Der Funktionsparameter
const std::initializer_list<const int>& list
ist keine Compile-Time Konstante und daher kann darauf keinstatic_assert
angewendet werden (auch wennsize()
seit C++14 alsconstexpr
deklariert ist).
Es wird ja nicht für jeden Aufrufstatic_assert
angewendet, sondern einmalig beim Kompilieren der Funktion (auch wenn diese nirgendwo aufgerufen werden würde).
-
Typisches XY Problem. Davon abgesehen basiert dein Code auf einem Denkfehler. Funktionsparameter können niemals als constant expressions behandelt werden, weil sie über mehrere Aufrufe eben nicht konstant sind. Wenn ich
constexpr Foo (const std::initializer_list<const int>& list) { MyTemplate<list.size()> t; }
schreiben könnte, wäre die Typisierung dieser Funktion nicht durchführbar. Es spricht im Uebrigen nichts dagegen, einfach eine Exception zu werfen. Der Konstruktor kann ja auch als consteval definiert werden, dann wird die exception in jedem Fall zu einem Uebersetzungsfehler fuehren.
class Foo { public: consteval Foo (const std::initializer_list<int>& list) { if (list.size() != 3) throw std::logic_error(""); } }; int main () { static constexpr std::initializer_list<int> list = {1, 2}; Foo f(list); }
https://coliru.stacked-crooked.com/a/612e4b1772124444
PS:
initializer_list<const int>
ist auchPPS: Das was im OP versucht wird kann auch mit
template <size_t N> Foo (const int (&arr)[N]) { static_assert (2 == N); }
erreicht werden, aber das wird ein unnötig verkorkstes Interface ergeben.
-
@Columbo sagte in Anzahl Elemente in initializer_list zum Übersetzungszeitpuntk begrenzen:
Typisches XY Problem. Davon abgesehen basiert dein Code auf einem Denkfehler. Funktionsparameter können niemals als constant expressions behandelt werden, weil sie über mehrere Aufrufe eben nicht konstant sind.
Nein, ein Denkfehler ist es nicht. Eher war ich zu optimistisch, dass es funktionieren könnte. Nur schlägt hier wieder Type Decay zu, in dem aus einem
static constexpr
Parameter einfach einconst
Parameter gemacht wird. Das sind so Dinge an modernen C++ die mich am Verstand der Entwickler zweifeln lässt. Es gibt nun consteval Funktionen, aber Parameter für solche Funktionen dürfen nicht constexpr sein?Wenn ich
constexpr Foo (const std::initializer_list<const int>& list) { MyTemplate<list.size()> t; }
schreiben könnte, wäre die Typisierung dieser Funktion nicht durchführbar.
Natürlich wäre sie das, und zwar immer dann wenn das übergebene Objekt
constexpr
bzw.list.size()
consteval
wäre. Dann wäre nämlich zum Übersetzungszeitpunktlist.size()
auswertbar, und würde eine entsprechende Non Type Template Parameter Spezialisierung liefern. Der Type Decay macht aber einen Strich durch die Rechnung. Nur das ist halt unsinnig und unlogisch, aber so vieles ist bei modernem C++ nicht logisch.Wenn man die
initializer_list
alsstatic constexpr
definiert, kann man dannMyTemplate<list.size()> t;
im normalen Code verwenden.Es spricht im Uebrigen nichts dagegen, einfach eine Exception zu werfen. Der Konstruktor kann ja auch als consteval definiert werden, dann wird die exception in jedem Fall zu einem Uebersetzungsfehler fuehren.
class Foo { public: consteval Foo (const std::initializer_list<int>& list) { if (list.size() != 3) throw std::logic_error(""); } }; int main () { static constexpr std::initializer_list<int> list = {1, 2}; Foo f(list); }
Wunderbar danach hatte ich gesucht, dieser Aspekt war mir noch nicht bewusst. Danke. Nur spuckt der g++ 11.2.0 wieder einmal vollkommen unsinnig Fehlermeldungen aus, wenn man den Konstruktor falsch nutzt.
https://coliru.stacked-crooked.com/a/612e4b1772124444
PS:
initializer_list<const int>
ist auchPPS: Das was im OP versucht wird kann auch mit
template <size_t N> Foo (const int (&arr)[N]) { static_assert (2 == N); }
erreicht werden, aber das wird ein unnötig verkorkstes Interface ergeben.
Darüber war ich auch schon gestolpert, aber das bedingt wieder andere Probleme.
-
@john-0 sagte in Anzahl Elemente in initializer_list zum Übersetzungszeitpuntk begrenzen:
Natürlich wäre sie das, und zwar immer dann wenn das übergebene Objekt constexpr bzw. list.size() consteval wäre. Dann wäre nämlich zum Übersetzungszeitpunkt list.size() auswertbar, und würde eine entsprechende Non Type Template Parameter Spezialisierung liefern.
Ja, und die Funktionsdefinition muesste dann jedes mal neu kompiliert werden, was daraus effektiv ein Template machen wuerde. Nur leider hast Du es nicht entsprechend deklariert.
Nein, ein Denkfehler ist es nicht. Eher war ich zu optimistisch, dass es funktionieren könnte.
Du bist 'optimistisch', dass eine systemische Limitierung der Sprache ploetzlich ausgesetzt wird? Ist es auch Optimismus, wenn ein geisteskranker vom Dach springt, weil er fliegen wollte? Oder hat er einfach Gravitation nicht verstanden?
PS: Es ist i.A. kein gutes Zeichen, seine eigene Ignoranz auf Fehldesign des betreffenden Systems zu schieben..
-
@Columbo sagte in Anzahl Elemente in initializer_list zum Übersetzungszeitpuntk begrenzen:
Ja, und die Funktionsdefinition muesste dann jedes mal neu kompiliert werden, was daraus effektiv ein Template machen wuerde. Nur leider hast Du es nicht entsprechend deklariert.
Hier denkt jemand nicht mit! Das Problem existiert doch exakt mit den
throw std::logic_error
in derconsteval
Funktion ebenfalls. Das lässt sich nur dann zum Compilezeitpunkt prüfen, wenn die Definition vorliegt und geprüft wird, und natürlich kann man keineconsteval
Funktion nutzen, ohne dass der Definition sichtbar ist. Der Compiler mahnt das sogleich an. Also, wo bitte ist das Problem constexpr Ausdrücke als Parameter zu erlauben?PS: Es ist i.A. kein gutes Zeichen, seine eigene Ignoranz auf Fehldesign des betreffenden Systems zu schieben..
Das schreibt der richtige. Du übersiehst die Problematik ebenfalls nicht vollständig, und haust hier trotzdem maximal überhebliche Kommentare raus. Etwas mehr Demut täte Dir gut.
-
Das Problem existiert doch exakt mit den throw std::logic_error in der consteval Funktion ebenfalls. Das lässt sich nur dann zum Compilezeitpunkt prüfen, wenn die Definition vorliegt
Verstehste den Unterschied zwischen 'pruefen' und 'uebersetzen'?
-
@Columbo sagte in Anzahl Elemente in initializer_list zum Übersetzungszeitpuntk begrenzen:
Verstehste den Unterschied zwischen 'pruefen' und 'uebersetzen'?
Mit dem Übersetzen ist es nicht getan. Die Funktionen müssen zum Übersetzungszeitpunkt ausgeführt werden, nur so weiß der Compiler, dass die Bedingungen erfüllt bzw. nicht erfüllt sind. Würde nur übersetzt, dann kann der Compiler die Ausdrücke nicht auswerten und prüfen, da die Parameter ja nicht als Übersetzungszeitpunkt konstant definierbar sind bzw. er müsste eigentlich auch die Variante mit throw verwerfen. Tut er aber nicht.