Extending the C string constant's lifetime with the reference isn't a good idea
It looks like returning a reference variable disables named return value optimization, at least in practice:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
#include <cstdio>
struct A
{
A() = default;
A(A const&) { std::puts("copied\n"); }
};
A f() { A const a; return a; }
A g() { A const& ra = A{}; return ra; }
int main()
{
g();
}
To my understanding the "standard" explanation for this behavior depends on whether a names the referent or the reference. https://eel.is/c++draft/class.copy.elision#1.1
I'm not certain about the standard's contents, but in practice there is no implementation divergence - C++20 compilers don't optimize g: https://godbolt.org/z/d5xTn6qE5
Are there more reasons why regularly relying on lifetime extension isn't a good idea?
Are you trying to explain to me that acting this way I am creating a temporary (or anonymous) object which is just useless in this context?
In the paper which shared with me @George P, I read : When this happens, a temporary object is created and initialized with the rvalue, and the reference to const is bound to that temporary object. A temporary object (also sometimes called an anonymous object) is an object that is created for temporary use (and then destroyed) within a single expression.
Geckoo the behavior of const std::string &winner = "Simple output";
Is
1. Convert the string literal into a std::string
2. Create a temporary object to hold the resulting string (i.e. "materialize a temporary")
3. Bind the reference to the temporary object, whose lifetime is extended so that winner doesn't become a dangling reference.
On the other hand, const std::string winner = "Simple output";
Only calls std::string's constructor. This is an example of copy-initialization.
Are you trying to explain to me that acting this way I am creating a temporary object which is just useless in this context?
Whether the object starts out temporary and then has its lifetime extended to match the reference's lifetime, or starts out as an object with automatic storage duration, doesn't make much difference in isolation.
It can make some difference to surrounding code, see my earlier post.
Maybe other folks can come up with more reasons why using references in this way might or might not be a good idea.
That's easy. This lets the derived classes override the function to customize the behavior as needed, without further exposing the virtual functions directly by making them callable by derived classes (as would be possible if the functions were just protected)... Only if derived classes need to invoke the base implementation of a virtual function, make the virtual function protected.
Hello. This thread is captivating. I have another question : in my previous code I use a pure virtual function in the base class. If I change it for a virtual function with a simple body, what will be changed in the runtime process? I read many different explanations on the web, but it is not always consistent... To be clear, what will be the difference between those alternatives (both they work fine in the previous code) ?
virtualvoid function() = 0; // pure virtual function
I guess by "pure virtual class" seeplus mean what is normally known as an "abstract class".
An abstract class is a class that either declares a pure virtual function or that inherits a pure virtual function without overriding it. As seeplus said, you cannot create instances (objects) of an abstract class.
Quite often it doesn't make sense to create instances of the base class and there is no default implementation that makes sense. By making the function pure virtual you force derived classes to provide their own implementation of the function (unless they intend to be abstract classes).
Abstract classes also avoid the problem of object slicing. Another way to avoid the problem is to disable copy/move construction and copy/move assignment but this is not necessary with abstract classes. https://en.wikipedia.org/wiki/Object_slicing
Thanks @Peter87. As I understand, an abstract class has at least one pure virtual function. In other words, a function that has no definition - only a declaration with 0. So the child must define the pure virtual function in their base class.
But I guess that "during the runtime" there is another behavior between a pure virtual class and a virtual class with a body. No? Is there an object for virtual function - when there is nothing for a pure virtual function?
> But I guess that "during the runtime" there is another behavior ...
> Is there an object for virtual function - when there is nothing for a pure virtual function?
An abstract class can be used only as a base class of some other class;
#include <iostream>
struct A { int v = 9 ; virtual ~A() = default ; } ;
struct B : A { virtual ~B() = 0 ; /* pure virtual destructor */ B() { std::cout << "B::constructor\n" ; } } ;
B::~B() { std::cout << "B::pure virtual destructor\n" ; }
struct C : B {} ;
int main()
{
A a ; // fine; A is not an abstract class
// B b ; // *** error *** B is an abstract class
C c ; // fine; C is not an abstract class. need to construct a sub-object of type B
[[maybe_unused]] B& b = c ; // fine; access sub-object of type B
// destroy C; destroy sub-object of type B
}
#include <iostream>
struct A {
virtual ~A() = default ;
virtualvoid pure() = 0;
} ;
struct B : A {} ;
struct C : A {
void pure() { A::pure(); }
} ;
int main()
{
// A a ; // error: variable type 'A' is an abstract class
// B b ; // error: variable type 'B' is an abstract class
C c ;
A& r = c;
r.pure();
}
void A::pure() {
std::cout << "Hello world\n";
}
I guess that "during the runtime" there is another behavior between a pure virtual class and a virtual class with a body. No?
No. There is no difference at runtime between a pure virtual function and a non-pure virtual function.
Is there an object for virtual function - when there is nothing for a pure virtual function?
I'm not sure that I understand this question. Creating objects (usually) happens at runtime but if you tried to create an instance of an abstract class the code would not even compile so the difference is not at runtime.
It's still pure. A is still an abstract class. Any class that inherits from A still needs to override it (unless it also wants to be an abstract class).
The only way you can call it is by qualifying the function call with A:: in front like keskiverto did on line 11.
Why would this be useful? I don't know. I suggest you ignore this quirk for the time being.
It is less about the "purity" of the member function and more about declaring the class being abstract. With the quirk one can provide an abstract class, yet include default implementations for all of its member functions.
I'd guess that the standardization committee does not like "artificial limitations".