This question already has answers here:
Will a reference bound to a function parameter prolong the lifetime of that temporary?
(4 answers)
const reference to a temporary object becomes broken after function scope (life time)
(2 answers)
Closed 4 years ago.
I was watching a video Curiously Recurring C++ Bugs at Facebook
at 14:58 at the code (see in the example I give here) he says that is is hopelessly broken. The reason is that returning reference to a temporary object should not work.
so why it works?
#include <iostream>
#include <map>
using namespace std;
const string& get_default(
const map<string,string>& map,
const string& key,
const string& dflt) {
auto pos = map.find(key);
return (pos != map.end() ?
pos->second : dflt);
}
int main()
{
map<string,string> m;
auto& value = get_default(m,"whatever","this is the default value");
cout << value << endl;
return 0;
}
I understand why I cannot return reference to a local variable (and that fails nicely when I try to print it), but I cannot make this code fail, and I dont know why.
I checked google and found that if temporary object is assigned to a reference the temporary objects lifetime will be extended (thats why it is working?).
If so why is this code so hopelessy broken?
Using: gcc 7.3.0, c++14
using the comments now I can see how to make it fail:
Thanks everyone :)
int main()
{
map<string,string> m;
auto& value = get_default(m,"whatever","this is the default value"); // const string& value = fails the same way
string a = "some long string";
cout << value << endl;//"some long string" will be printed here (this is how it fails - and shows that it is a mess)
return 0;
}
The life time of a temporary object is extended only in some contexts. The example in the posted code is not one of those contexts.
int foo() { return 10; }
...
int const& x = foo(); // Life of the temporary is extended.
However,
const int& foo() { return 10; } // Life of the temporary is not extended.
...
int const& x = foo(); // Dangling reference.
Your posted code is analogous to
const int& foo(int const& in) { return in; }
...
int const& x = foo(10);
Here, the reference is valid only inside foo. Once foo returns, the object is not alive any longer, and the reference becomes a dangling reference.
Related
I'm watching this talk from cppcon17 and am trying to replicate and understand the presented bugs.
However, I'm not able to replicate/understand bug #3 in the talk (timestamp is in the link above). I've written this minimal example:
#include <iostream>
#include <map>
const std::string& getDefault(const std::map<int, std::string>& map, const int key, const std::string& defVal) {
auto pos = map.find(key);
return (pos != map.end() ? pos->second : defVal);
}
void printer(const std::string& str) {
std::cout << str << std::endl;
}
int main () {
std::map<int, std::string> myMap { {1, "dog"}, {2, "cat"}};
auto& retVal = getDefault(myMap, 3, "pizza");
printer(retVal);
return 0;
}
As I understand the problem, the temporary default value of "pizza" should not be returned by reference, because it should get deleted.
But why does this example work anyway? I was expecting an error due to a reference that is no longer valid.
Edit: I've modified the example slightly to highlight that the problem still occurs, if the temporary is on another line.
This code, i.e. std::cout << getDefault(myMap, 3, "pizza") << std::endl; is valid; the temporary will be destroyed after the full expression, i.e. after the ;, the returned reference remains valid when being passed to and printed out by operator<< of std::cout.
All temporary objects are destroyed as the last step in evaluating the full-expression that (lexically) contains the point where they were created,
EDIT
Your updated code has undefined behavior. After the ; the temporary object has been destroyed and retVal is dangled; any dereference on it leads to UB.
This question already has answers here:
Why does calling std::string.c_str() on a function that returns a string not work?
(3 answers)
Closed 2 years ago.
I am new to C++ programming (work with Java mostly), and this behavior of C++ classes, member strings and string conversions to const char* with c_str() is confusing me.
I have a header, a class and main function as follows:
sample.h
class Sample
{
private:
int id;
std::string text;
public:
Sample(int id);
void setId(int id);
int getId();
void setText(std::string txt);
std::string getText();
void loadText();
~Sample();
}
sample.cpp
Sample::Sample(int id)
{
this->id = id;
}
void Sample::setId(int id)
{
this->id = id;
}
int Sample::getId()
{
return this->id;
}
void Sample::setText(std::string txt)
{
this->text = txt;
}
std::string Sample::getText()
{
return this->text;
}
void Sample::loadText()
{
this->text = "Loaded";
}
Sample::~Sample()
{
std::cout << "Destructor is called." << std::endl;
}
main.cpp
void main()
{
int id = 1;
Sample* sample = new Sample(id);
// Case: 1 - If I do this, it does not work. Prints gibberish.
sample->loadText();
const char* text = sample->getText().c_str();
std::cout << text << std::endl;
// Case: 2 - Otherwise, this works.
sample->loadText();
std::cout << sample->getText().c_str() << std::endl;
// Case: 3 - Or, this works
sample->loadText();
std::string txtCpy = sample->getText();
const char* text = textCpy.c_str();
std::cout << text << std::endl;
}
All three cases are done one at a time.
Case 3 does satisfy my use case (which is, passing the string to a C library that expects a const char*. But, I can't figure out the difference between Case: 1 and Case: 3? If we are returning the string by value, how does copying it to an intermediate variable make it kosher for the run-time?
The result of c_str() is only valid while the string you called it on still exists. In case 3, txtCpy still exists at the point you are writing cout << text. But in Case 1, the string was the return value of sample->getText which is temporary and stop existing at the end of that line .
This issue always will exist if you take pointers or references to other objects. A naked pointer or reference has its own lifetime which may differ from the lifetime of the targeted object. This is unlike Java where object references all participate in the lifetime of the object.
As such, you always need to think about object lifetimes when using these features, and it's commonly recommended to instead use higher level features or other code styles that do not permit lifetime management errors.
You could consider adding a member function to Sample which gets a const char * pointing at the original string (although this is a wee violation of encapsulation, and still has a similar class of problem if you hold onto the pointer and then modify the underlying string). Better would be to just avoid working with the naked pointers entirely.
In this code snippet
const char* text = sample->getText().c_str();
std::cout << text << std::endl;
the variable text is assigned by a pointer (c_str()) of a temporary object returned from the member function getText. After this statement the temporary object will nit be alive, So the variable text has an invalid pointer,
The code snippet could be valid if the member function returned reference to the data member text like for example
const std::string & Sample::getText() const
{
return this->text;
}
Pay attention to that this declaration of main
void main()
is not a standard declaration.
The standard declaration of main without parameters is
int main()
This question already has answers here:
is there issue will stringstream.str().c_str()? [duplicate]
(3 answers)
Closed 6 years ago.
Consider the following code, based on this answer:
#include <iostream>
#include <sstream>
class StringBuilder {
public:
template <typename T> inline StringBuilder &operator<<(T const &t) {
ss << t;
return *this;
}
inline char const * c_str() {
return ss.str().c_str();
}
private:
std::stringstream ss;
};
void foo(const char *x) {
std::cout << x << std::endl;
}
int main() {
foo((StringBuilder() << "testing " << 12 << 3 << 4).c_str());
return 0;
}
Does calling foo() with the temporary StringBuilder's return value cause UB in any way?
The reason I'm asking is that the example above works great, but in real life I'm using a library that, amongst other things, contains logging facilities, and using this library I'll get incorrect output (the logging function takes my char* correctly but overwrites it internally, which caused me to believe that the memory is no longer valid).
Yes, but not because of what you perhaps think.
The temporary StringBuilder in the function call is not destroyed until after foo returns, so that's fine.
However, the c_str() method returns the result of calling .str().c_str(), and the temporary string returned by this str() is destroyed as StringBuilder::c_str() returns, which means that the pointer returned is invalid outside. Using this pointer causes UB.
Let's look at the following C++ code:
#include <iostream>
int main()
{
int z = 2;
class A {
public:
const int & x;
A(const int & x) : x(x) {}
void show(){
std::cout << "x=" << this->x << std::endl ;
}
} a(z);
a.show();
z = 3;
a.show();
}
The program prints: 2 and 3
It clearly shows that while inside class A x can't be modified, it merely means it's read only, because I can change it's value from outside.
Of course I can make it a copy stored inside class A, but I'm wondering if there is (or if there is a proposal?) of a way to say to class A that the member x will be truly constant instead of merely read only, with the meaning of a promise that the external code won't change it ?
To my eyes it looks like something related to the meaning of the C restrict keyword, but I've not heard of any such C++ feature yet. Do you ?
Constness is an attribute of the actual variable.
The term const int& x simply means "x is a reference to an int which it will not modify" and of course the compiler enforces this.
If you want the actual variable to which x refers to be const, simply declare it so:
#include <iostream>
int main()
{
const int z = 2; // declared const. Nothing may ever modify it
class A {
public:
const int & x;
A(const int & x) : x(x) {}
void show(){
std::cout << "x=" << this->x << std::endl ;
}
} a(z);
a.show();
z = 3; // this is a logic error, caught by the compiler.
a.show();
}
compiling correctly produces the error:
./const.cpp:41:7: error: read-only variable is not assignable
z = 3;
~ ^
1 error generated.
You're looking for D's immutable keyword, which was introduced as a new concept in that language precisely because, unfortunately, the answer is no: it does not exist in C++.
Constness in C++ does not mean immutability, but that the variable in question is read-only. It can still be modified by other parts of the program. I understand your question as to whether it's possible to enforce true immutability in a called function without knowing what the caller is doing.
Of course you can create a template wrapper class which accomplishes the task:
template <typename T>
class Immutable
{
public:
template <typename ...Args>
Immutable( Args&&...args )
: x( std::forward<Args>(args)... )
{}
operator const T &() const
{
return x;
}
private:
const T x;
};
As long as you do not reinterpret_cast or const_cast you will have truly immutable objects when you wrap them with Immutable<T>.
However, if you have a constant reference to some object, there is no way to tell, if some other part of the program has a non-constant access to the object. In fact, the underlying object might be a global or static variable, that you have read-only access to, but functions you call might still modify it.
This cannot happen with Immutable<T> object. However, using Immutable<T> might impose an extra copy operation on you. You need to judge yourself if you can live with that and if the cost justifies the gain.
Having a function require an const Immutable<Something> & instead of const Something & as an argument affects the calling code. A copy operation might be triggered. Alternatively, you can ask for an Immutable<Something> & without the const. Then no accidental copies will be triggered, but the calling code must pass a reference to Immutable<Something> object. And rightly so, because if the caller received a const & as an argument then the caller does not know, whether the object might get modified by someone else in the program. The caller has to create the object itself or require an immutable object to be passed to it as a reference.
Your original question
Here's your original problem with Immutable<int> & instead of const int &.
#include <iostream>
int main()
{
Immutable<int> z = 2;
class A {
public:
const Immutable<int> & x;
A(Immutable<int> & x) : x(x) {}
void show(){
std::cout << "x=" << this->x << std::endl ;
}
} a(z);
a.show();
//z = 3; // this would fail
a.show();
}
An other example
Here's how it works: If you write
void printAndIncrementAndPrint( int & i1, const int & i2 )
{
std::cout << i2 << std::endl;
++i1;
std::cout << i2 << std::endl;
}
int main()
{
int i = 0;
printAndIncrementAndPrint( i, i );
}
then it will print
0
1
into the console. If you replace the second argument of printAndIncrementAndPrint() with const Immutable<int> & i2 and keep the rest the same, then a copy will be triggered and it will print
0
0
to the console. You cannot pass and Immutable<int> to the function and a int & to the same underlying data without breaking the typesystem using const_cast or reinterpret_cast.
I think this is a design problem for the programmers, not the language. A const variable means for any user of that variable, they should not change the value of that variable. Our compiler is smart enough to help us make sure of that. So A is a user of z and if you want A know that A::x references to a const variable, then you should make z a const int. The const reference is just to keep the contract between the user and the provider.
Some hours ago I read this question in which the user initialized a const object as
const QList warnings = QList() << 0 << 3 << 7;
Indeed, this expression somehow hurt me because going against the concepts I have of const and of overloading operator<<.
I tried to define an operator<< possibly allowing for the mentioned expression and here is what I got:
typedef vector<int> Vect;
Vect operator<<(Vect v, const int i){ //ByVal v, This way it works, but at which cost?
v.push_back(i); //v is copied at every call!
return v;
};
//Here is where I build the object
const Vect const_vect = Vect() << 1 << 2;
//it works fine but I see massive overhead due to copies..#_#
//Just for testing
int main() {
for(auto e = const_vect.begin(); e!=const_vect.end() ; ++e){
cout << *e << " ";
}
return 0;
}
The previous code (which is also here) works fine.
The following, on the other hand, is the expression I would have expected from the definition of operator<<:
Vect& operator<<(Vect& v, const int i){ //ByRef v<-this I would have expected;
v.push_back(i); //however it can't work because receives and
return v; //returns reference to temporary object.
};
My question is: what am I missing? is the operator<< defined in QList differently implemented from mine, in particular, can it receive and return ByRef?
Secondly, is perhaps the aforementioned a standard procedure to initialize complex const objects?
Thirdly (maybe inconvenient), how would such an expression handled by the compiler? what is done at compile-time and what at run-time?