Binding a temporary to a const lvalue reference fails



  • Ich weiß nicht, wieso ich bei folgendem Beispiel einen segfault bekomme

    #include <iostream>
    #include <string>
    
    struct Base
    {
        virtual ~Base() = default;
        std::string name{"test"};
        virtual bool hasState() const {
            std::cout << "base" << std::endl;
            return !name.empty();
        }
    };
    
    struct Derived : public Base
    {
        bool hasState() const override {
            std::cout << "derived" << std::endl;
            return false;
        }
    };
    
    const Base &foo() {
        return Derived{};
    }
    
    int main() {
        Base const &ref = foo();
        if (ref.hasState())
            std::cout << "Base class" << std::endl;
        else
            std::cout << "Derived class" << std::endl;
    

    kann mir jemand helfen?

    funktioniert dynamic binding nicht bei lifetime extensions temporärer Objekte?



  • d ist kein temporäres Objekt.



  • ok ein local Objekt,trotzdem gleiche Frage



  • Sewing schrieb:

    ok ein local Objekt,trotzdem gleiche Frage

    Verboten, UB, Programm kaputt?



  • hilfreich!



  • d ist eine lokale Variable. Wenn der Scope verlasesn wird, also bei }, wird die lokale Variable zerstört. d ist also tot, aber du gibst eine Referenz darauf zurück.



  • ja, aber eine referenz to const, das heißt, das temporary oder local bleibt so lange erhalten, bis die const referenz out of scope geht



  • Sewing schrieb:

    ja, aber eine referenz to const, das heißt, das temporary oder local bleibt so lange erhalten, bis die const referenz out of scope geht

    Ne, eine lokale Variable bleibt da nicht erhalten, nur eine temporäre Variable. Da hast du etwas falsch verstanden.



  • ok danke, aber wenn ich das Ganze mit einem temporary mache (habe das Beispiel oben angepasst), dann kriege ich ebenfalls nen segfault



  • Sewing schrieb:

    ok danke, aber wenn ich das Ganze mit einem temporary mache (habe das Beispiel oben angepasst), dann kriege ich ebenfalls nen segfault

    Ja, das geht auch mit einer temporären Variablen nicht. Du hast nämliche einen wichtigen Satz übersehen: "Das bezieht sich nur auf die erste Referenz". Wenn die erste Referenz out of scope geht, ist die Variable tot.



  • mit anderen Worten: Liketime extension durch const references sind nicht translatorisch?



  • Sewing schrieb:

    mit anderen Worten: Liketime extension durch const references sind nicht translatorisch?

    Ja. Das Wort heißt aber transitive.

    Die Referenz, die foo zurückgibt, geht out of scope und die Variable ist tot.



  • und wieso bekomme ich dann bei

    int main() {
        Base obj{foo()};
    

    einen segfault obwohl ich copy initialize und bei

    int main() {
       const Base & obj{foo()};
    

    nicht, sondern erst wenn ich die member function auf der Referenz calle?



  • Na, beim ersten erzeugst du ja ein neues Objekt. Das kann er aber nicht machen, weil zum Zeitpunkt der Erzeugung die Referenz, die foo zurückgibt, schon out of scope und das temporäre Objekt damit tot ist.



  • ja und bei der zweiten Variane erzeuge ich nen alias auf ne tote Referenz, das geht aber?

    Hintergrund ist hier, dass ich das "Special Object Pattern" ausprobieren wollte.

    Ich suche also nach einer Instanz einer Klasse und wenn diese nicht aufzufinden ist, gebe ich ein Objekt einer Subklasse zurück (simuliert in der Funktion foo). Die Klassenhierarchie hat eine virtual methode, mit der ich erfragen möchte, ob ein "Nullobjekt", in diesem Fall Derived zurückgegeben wurde, oder ob die Instanz gefunden wurde. Unter den von dir genannten Gesichtspunkten weiß ich momentan nicht, wie ich das realisieren sollte mittels Referenzen



  • **Lese gerade ein Buch und in dem heißt es:
    **

    What would be much more comfortable is a return value that can be queried about its origination cause, and about what can be done with it. The answer is the Special Case pattern! The idea behind the Special Case pattern is that we take advantage of polymorphism, and that we provide
    classes representing the special cases, instead of returning nullptr, or some other odd value. These special
    case classes have the same interface as the “normal” class that is expected by the callers. The class(es) representing a special case are derived from class Customer.
    In C++ source code, an implementation of the Customer class and the NotFoundCustomer class
    representing the special case looks something like this (only the relevant parts are shown):

    #ifndef CUSTOMER_H_
    #define CUSTOMER_H_
    #include
    #include
    #include
    #include
    "Address.h"
    "CustomerId.h"
    <memory>
    <string>
    class Customer {
    public:
      // ...more member functions here...
      virtual ~Customer() = default;
      virtual bool isPersistable() const noexcept {
        return (customerId.isValid() && ! forename.empty() && ! surname.empty() &&
          billingAddress->isValid() && shippingAddress->isValid());
      }
    private:
      CustomerId customerId;
      std::string forename;
      std::string surname;
      std::shared_ptr<Address> billingAddress;
      std::shared_ptr<Address> shippingAddress;
    };
    class NotFoundCustomer final : public Customer {
    public:
      virtual bool isPersistable() const noexcept override {
        return false;
      }
    };
    using CustomerPtr = std::unique_ptr<Customer>;
    #endif /* CUSTOMER_H_ */
    

    The objects that represent the special case can now be used largely as if they were valid (normal)
    instances of class Customer. Permanent null-checks, even when the object is passed around between different
    parts of the program, are superfluous, since there is always a valid object. Many things can be done with the
    NotFoundCustomer object, as if it were an instance of Customer, for example, presenting it in a user interface.
    The object can even reveal whether it is persistable. For the “real” Customer, this is done by analyzing its data
    fields. In the case of the NotFoundCustomer, however, this check has always a negative result.
    And compared to the meaningless null-checks, a statement like the following one makes significantly
    more sense:

    if (customer.isPersistable()) {
      // ...write the customer to a database here...
    }
    

    Nur verstehe ich dann eben nicht, wie das Special-Case Object, dass ja durch ne referenz zurückgegeben wird in dem Beispiel offensichtlich, mit dynamic binding verwendet werden kann hier...



  • Sewing schrieb:

    ja und bei der zweiten Variane erzeuge ich nen alias auf ne tote Referenz, das geht aber?

    Natürlich geht das, du fasst den Speicherbereich der toten Variable nicht an.

    Das Pattern sagt mir nichts. Aber bezieht sich die Suche nicht auf ein Element innerhalb eines Containers... du brauchst ja etwas, in dem du suchen kannst? Sehe den Sinn sonst nicht.



  • Der Sinn ist doch

    CustomerPtr customer = make_unique<NotFoundCustomer>; 
    customer->isPersistable()
    

    Wobei make_unique eigentlich die die Suche in einer Datenank darstellen soll. Es wird nichts gefunden und NotFoundCUstomer wird zurückgegebn.



  • ja genau, mit Pointern geht das, das Buch nennt aber den .Operator für den Zugriff auf die Methode, dh. ich muss hier von einer Referenz ausgehen und das ist so dann ja nicht möglich oder?

    das Pattern soll dabei helfen, nullptr als return value zu vermeiden, da diese dem Autor nach, die Verantwortlichkeit der Überprüfung auf nullptr und dessen Deutung auf die Callsite abschiebt.

    Buch ist ne Neuerscheinung und heißt Clean C++
    https://www.amazon.de/Clean-Sustainable-Software-Development-Practices/dp/1484227921/ref=sr_1_1?ie=UTF8&qid=1516908031&sr=8-1&keywords=clean+c%2B%2B



  • Verstehe.

    Nein, das geht dann nicht.


Anmelden zum Antworten