Binding a temporary to a const lvalue reference fails



  • 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.



  • 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

    Das geht nur über Referenz, wenn die Objekte (inklusive dem special case object) irgendwo anders am Leben erhalten werden, z.B. in einem Container.

    Wenn dich der Punkt wirklich mental fertig macht, kannst du meinetwegen das machen:

    CustomerPtr pcustomer=...
    auto& customer=*pcustomer;
    if (customer.isPersistable())...
    


  • eine statische Variable wäre auch noch eine Idee



  • ;seh schrieb:

    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

    Das geht nur über Referenz, wenn die Objekte (inklusive dem special case object) irgendwo anders am Leben erhalten werden, z.B. in einem Container.

    Wenn dich der Punkt wirklich mental fertig macht, kannst du meinetwegen das machen:

    CustomerPtr pcustomer=...
    auto& customer=*pcustomer;
    if (customer.isPersistable())...
    

    wieso mental fertig macht? ich habe nur den fehler im buch entdeckt



  • Sorry, ich sehe den Fehler nicht.

    Da steht ja was von "statement like the following" - es ist also klar, dass es sich hier um ein Beispiel handelt. Vielleicht entspringt dieses Beispiel einer Funktion saveCustomer(const Customer& customer) . Der Punkt ist ja gerade, dass man keine nullptr hat und man daher eine solche Funktion immer aufrufen kann. Wäre der Parameter ein const Customer* , hätte man doch wieder einen zwangweisen nullptr-Check. Ich halte es also sogar für besser so, wie es im Buch steht.


Anmelden zum Antworten