Folgen von constexpr Konstruktor ohne constexpr Variable



  • Wenn ich eine Klasse Foo habe, deren ctor constexpr ist, ... soweit ich weiss kann ich dann das Ding ja irgendwo global/static mit constexpr instanzieren, und kann sicher sein dass es keine dynamische Initialisierung dafür geben wird. D.h. kein static initialization order fiasco.

    Soweit richtig?

    Was ist aber nun wenn ich eine Foo Instanz brauche die nicht const sein kann, aber trotzdem sicher sein will dass keine dynamische Initialisierung passiert? Reicht es dazu dass der verwendete Foo Ctor constexpr ist?

    Falls nicht klar ist was ich meine...

    struct Foo {
        constexpr Foo(int i) : j(2 * i) {}
        int j;
    };
    
    Foo f{ 21 }; // Darf das hier jemans zu dynamischer Initialisierung führen?
    
    Foo* getFoo() {
        return &f;
    }
    


  • Ich hab' jetzt nicht nachgesehen, aber warum sollte constexpr Einfluss auf die Initialization Order haben? Vielleicht stehe ich auf diversen Leitungen, aber wie kommst Du darauf?



  • @Swordfish Naja wenns keine dynamische Initialisierung gibt, dann gibt's auch keine Order, und dann kanns auch kein Problem mit der Order geben. Nen?



  • Was ist denn "dynamische Initialisierung"? Ist das Gegentum davon "statische Initialisierung"? Aber, was ist das? constexpr hilft Dir nicht zur Umgehung des static initialization order Fiaskos.


  • Mod

    @hustbaer Der Standard garantiert es tatsaechlich.

    Constant initialization is performed if a variable or temporary object with static or thread storage duration is initialized by a constant initializer ([expr.const]) for the entity.

    Es ist recht offensichtlich, dass jegliche Eigenschaften eines Initializers nichts mit der constness des zu initialisierenden Objekts zu tun haben.

    Du kannst diese Eigenschaft aber testen, es scheint als ob GCC sich nicht an den Standard haelt (es gab aehnliche Bugs wie https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67550)

    #include <type_traits>
    
    struct Foo {
        constexpr Foo(int i) : j(2 * i) { if(!std::is_constant_evaluated()) throw; }
        int j;
    };
    
    Foo f{ 21 }; // Darf das hier jemans zu dynamischer Initialisierung führen?
    
    Foo* getFoo() {
        return &f;
    }
    
    int main() {}
    


  • Könnte mich mal jemand aufklären? Entweder stehe ich auf diversen Leitungen oder ich kapiere die "magic" wirklich nicht.



  • @Swordfish
    Dynamische Initialisierung:

    int global = rand();   // Erzeugt einen Library-Konstruktor
    

    Konstante Initialisierung:

    int global = 42;       // Erzeugt bloss ein paar Bytes im .data Segment
    

    "Statische Initialisierung" ist vielleicht wirklich irreführend.



  • @Swordfish sagte in Folgen von constexpr Konstruktor ohne constexpr Variable:

    constexpr hilft Dir nicht zur Umgehung des static initialization order Fiaskos.

    Doch, natürlich.
    Weil wenn alles schon fertig im .exe/.dll/.so/... File drinnen steht, dann gibt's nix mehr zu initialisierung und daher auch kein Problem mit einer Reihenfolge.



  • @hustbaer sagte in Folgen von constexpr Konstruktor ohne constexpr Variable:

    "Statische Initialisierung" ist vielleicht wirklich irreführend.

    Warum?
    Das Problem der statischen Initialisierung, lässt sich z.B. mit einem Singleton lösen. Das Singleton wird statisch initialisiert, und alle anderen Übersetzungseinheiten können per statischer Methode auf das Singleton zugreifen. Da der erste Zugriff die Initialisierung auslöst, falls noch nicht erfolgt, ist die Reihenfolge der Übersetzungseinheiten vollkommen irrelevant.

    // foo.h
    #include <iostream>
      
    class Foo {
        int a_;
    
        Foo ();
        ~Foo();
        Foo(Foo&) = delete;
        Foo(Foo&&) = delete;
        Foo& operator= (Foo&) = delete;
        Foo& operator=(Foo&&) = delete;
    public:
        static Foo& instance(char const* const name);
    };
    
    // foo.cc
    #include <iostream>
    #include <cstddef>
    #include <string>
    
    #include "foo.h"
    
    Foo::~Foo() {}
    
    Foo::Foo() : a_(21) {
        std::cout << "Foo::Foo()\n";
    }
    
    Foo&
    Foo::instance(char const*const name) {
        std::cout << "called from " << name << "\n";
        static Foo instance;
    
        return instance;
    }
    
    
    Foo& foo_f = Foo::instance("foo.cc");
    
    // test1.cc
    #include "foo.h"
    
    static Foo& test1_f = Foo::instance("test1.cc");
    
    
    // main.cc
    #include <iostream>
    #include <cstddef>
    
    #include "foo.h"
    
    static Foo& main_f = Foo::instance("main.cc");
    
    
    int main () {
        std::cout << "main.cc\n";
    
        return EXIT_SUCCESS;
    }
    
    
    #makefile
    OFILES_1=test1.o main.o foo.o
    OFILES_2=test1.o foo.o main.o
    OFILES_3=main.o test1.o foo.o
    OFILES_4=main.o foo.o test1.o
    OFILES_5=foo.o main.o test1.o
    OFILES_6=foo.o test1.o main.o
    
    CCFLAGS=-g -O0
    CCC=g++
    
    all: programm1 programm2 programm3 programm4 programm5 programm6
    
    run:
        ./programm1
        ./programm2
        ./programm3
        ./programm4
        ./programm5
        ./programm6
    
    programm1: $(OFILES_1)
        g++ $(OFILES_1) -o programm1
    
    programm2: $(OFILES_2)
        g++ $(OFILES_2) -o programm2
    
    programm3: $(OFILES_3)
        g++ $(OFILES_3) -o programm3
    
    programm4: $(OFILES_4)
        g++ $(OFILES_4) -o programm4
    
    programm5: $(OFILES_5)
        g++ $(OFILES_5) -o programm5
    
    programm6: $(OFILES_6)
        g++ $(OFILES_6) -o programm6
    
    .SUFFIXES: .cc .c .h
    
    clean:
        rm -rf *.o programm1 programm2 programm3 programm4 programm5 programm6
    
    .cc.o:
        $(CCC) $(CCFLAGS) -o $*.o -c $<
    
    test1.o: test1.cc foo.h
    main.o: main.cc foo.h
    foo.o: foo.h foo.cc
    
    


  • @hustbaer sagte in Folgen von constexpr Konstruktor ohne constexpr Variable:

    Doch, natürlich.

    Na schön. Ich gebe zu mich mit constexpr declarations nicht wirklich beschäftigt zu haben. Nur wenn man sich die constraints ansieht ergibt sich dadurch wirklich nur ein sehr eingeschränkter Nutzen wenn es darum geht das Static Initialization Order Fiasco zu umgehen: [decl.constexpr]/9.



  • @Swordfish sagte in Folgen von constexpr Konstruktor ohne constexpr Variable:

    Nur wenn man sich die constraints ansieht ergibt sich dadurch wirklich nur ein sehr eingeschränkter Nutzen wenn es darum geht das Static Initialization Order Fiasco zu umgehen: [decl.constexpr]/9.

    Ne. Genau darum ging's mir ja in der Frage: Ob es erforderlich ist dass das Objekt selbst constexpr gemacht wird, oder ob es reicht dass der verwendete ctor constexpr ist. Und wenn ich @Columbo richtig verstanden habe dann reicht es wenn der verwendete ctor constexpr ist.

    D.h. man kann globale/statische SpinLocks und was nicht noch alles schönes (*g*) machen ohne dabei ins Static Initialization Order Fiasco zu rutschen -- vorausgesetzt der ctor dieser schönen Sachen ist constexpr. Ob die Objekte selbst constexpr sind ist dabei wörscht.




Log in to reply