Transparently insert temporary into caller's scope - c++

In C++, operator-> has special semantics, in that if the returned type isn't a pointer, it will call operator-> again on that type. But, the intermediate value is kept as a temporary by the calling expression. This allows code to detect changes in the returned value:
template<class T>
class wrapper
{
// ...
T val;
struct arrow_helper
{
arrow_helper(const T& temp)
: temp(temp){}
T temp;
T* operator->() { return &temp; }
~arrow_helper() { std::cout << "modified to " << temp << '\n'; }
};
arrow_helper operator->() { return{ val }; }
//return const value to prevent mistakes
const T operator*() const { return val; }
}
and then T's members can be accessed transparently:
wrapper<Foo> f(/*...*/);
f->bar = 6;
Is there anything that could go wrong from doing this? Also, is there a way to get this effect with functions other than operator->?
EDIT: Another issue I've come across is in expressions like
f->bar = f->bar + 6;
since when the arrow_helper from the second operator-> is destructed it re-overwrites the value back to the original. My semi-elegant solution is for arrow_helper to have a T orig that is hidden, and assert(orig == *owner) in the destructor.

There is no guarantee that all changes will be caught:
Foo &x = f->bar;
x = 6; /* undetected change */

If there is no way to grab a reference to any data within T through T's interface or otherwise, I think this should be safe. If there's any way to grab such a pointer or reference, you're done and in undefined behavior as soon as someone saves off such reference and uses it later.

Related

Strange behavior of reference in c++

I'm little confused about reference type in c++, here goes my code snippet.
class data
{
public:
std::vector<int> Get() const
{
return vec;
}
private:
std::vector<int> vec = {1,2,3,4};
};
int main()
{
data dat;
auto const& a = dat.Get()[1];
auto const& b = dat.Get()[2];
std::cout << "a = " << a << ", b = " << b << std::endl;
return 0;
}
The output a = 0, b = 1433763856 doesn't make any sense, after I remove the leading & before a and b, everything works fine. Now here goes my questions:
Since reference of reference is not allowed, but vector::operator[] do return a reference of element inside container, why no error thrown?
I know data::Get() function causes deep copy, but why I get the wrong value of a and b? Will the return value be destroyed right after function call?
You return a copy of the vector, as the signature
std::vector<int> Get() const
implies, as opposed to
std::vector<int> /*const*/& Get() const
which would return a reference, this is true, but that doesn't really explain why returning a copy is a mistake in this situation.
After all, if the call was
auto const& v = data.Get(); // *your* version here, the one returning by copy
v would not be dangling.
The point is that you're not keeping that copy alive by bounding it to a reference (as I've done in the last snippet).
Instead, you're calling operator[] on that temporary, and that call results in a reference to an entry (int&) in that vector. When the temporary vector returned by dat.Get() is destroyed, that's the reference which dangles.
If operator[] returned by value, then not even the a and b in your example would dangle.

Understanding rvalue reference on return

For example i have code such as below
//g++ 5.4.0
#include <iostream>
struct data
{
int n;
data()
{
std::cout << "data()\n";
}
data(const data&)
{
std::cout << "data(const data&)\n";
}
data(data&&)
{
std::cout << "data(data&&)\n";
}
};
class container
{
data d;
public:
data getData()
{
return std::move(d);
}
};
int main()
{
container c;
data result = c.getData();
}
And output is:
data()
data(data&&)
I don't understand how it works. I have not declared return type as data&&, but move constructor works fine for result. Yes, code is std::move(d) but return type is not data&&. So, how does it work?
This answer changes in c++17.
data getData()
{
return std::move(d);
}
This method moves d into its return value.
data x = foo.getData();
this one constructs x from the return value of getData. However the C++ standard encourages and permits this construction to be elided if getData returns a prvalue (a value type that matches). Elision means that the identity and lifetime of the return value and x are merged. Only one object exists, not two.
This permits skipping side effects, like print statements in move constructors.
So d is the object moved from, and that move directly constructs x.
If you change getData to return data&&, now no move is done withingetData but one is done outside when you construct x.
In c++17 the return value of getData is never an object, it is a prvalue, and prvalues in c++17 are more like instructions to create objects. In effect elision is no longer optional.
If the return type is set to data (as in your case), then the returned object is a prvalue.
If the return type is set to data&&, then the returned object is an xrvalue.
In either case, the returned object is an rvalue, and result's move constructor will be called.
See also: http://stackoverflow.com/a/10159163/4509057

How to create a temporary variable in C++

I have a function returning a reference to an instance of my class "record".
record& get_record(int key) {
return lookup(key);
}
That is effective it returns a reference and not by value. Now I modify it a bit.
record& get_record(int key) {
if (valid(key))
return lookup(key);
else {
record x;
x.valid=false;
return x; //Here I really want to return a temporary variable
// and not a reference to a local variable.
}
}
Is it correct that returning a reference to a local variable is not a good idea? and how do I return x in such a way that it becomes a temporary variable?
This is worse than a bad idea, it is undefined behavior and result in most of the cases to a crash. This is bad (TM).
What you could do is changing the return type of get_record so it returns a smart pointer. If key is valid, it returns an observer pointer to it. Otherwise, it returns an owning smart pointer to a freshly created object:
#include <memory>
#include <iostream>
struct record { int n; } some_record{42};
std::shared_ptr<record> get_record(bool b)
{
if (b == true) {
return std::shared_ptr<record>{&some_record, [](record*){}}; // see explanation ^1
}
return std::shared_ptr<record>{new record{0}};
}
int main()
{
std::cout << get_record(true)->n << "\n"; // this is some_record
std::cout << get_record(false)->n << "\n"; // this is a temporary
}
1) About [](record*){}: this no-op lambda given as the second argument to std::shared_ptr::shared_ptr() is invoked when the smart pointer is destroyed. It replaces the default deleter of std::shared_ptr whose behavior is to call delete on the owned pointer.
About why your design is flawed. In fact, making get_record return a reference makes it not consistent. What you want is:
if key is valid return a reference to an existing/permanant object, and
return a temporary object otherwise.
Those two are mutually exclusive, and your function doesn't make sense: what does get_record return semantically?
Is it correct that returning a reference to a local variable is not a good idea?
Yes. The local object will be destroyed when get out of the function so the returned reference is always dangled.
You might make x a static variable.
record& get_record(int key) {
if (valid(key))
return lookup(key);
else {
static record x;
x.valid=false;
return x;
}
}
Note that the returned reference will always refer to the same object, i.e. x.
If you are allowed to modify the get_record function you can change the return type to pointer to record instead of reference to record.
record* get_record( int key )
{
if( valid( key ) ) return &lookup( key );
else return nullptr;
}
However, this approach needs two guarantees:
the lookup function must return a reference to record
the record returned by lookup must still live when lookup returns (e.g. record is an element of some sort of container and lookup returns its reference)
As others already detail why returning a reference to a local is bad, ill just provide an alternative: Exceptions. Though you could write a custom exception, arguably an std::out_of_range exception would seem in place (as the key lies outside of the range of valid keys, which is exactly what std::map::at does).
Have a look at: How to throw a C++ exception
record& get_record(int key)
{
if (valid(key))
{
return lookup(key);
} else {
throw std::out_of_range("The provided key is invalid");
}
}
You will now, obviously, have to catch the exception in the code calling get_record, otherwise your program will still terminate.
Returning a reference in itself doesn't produce undefined behaviour, but if you attempt to modify it, then you will.
Accessing an object outside of its lifetime is undefined behavior.
int* foo(void) {
int a = 17; // a has automatic storage duration
return &a;
} // lifetime of a ends
int main(void) {
int* p = foo(); // p points to an object past lifetime ("dangling pointer")
int n = *p; // undefined behavior
}
http://en.cppreference.com/w/c/language/lifetime
If you have access to C++17, you could implement it using std::optional. Note the use of std::reference_wrapper, because use of a reference in std::optional makes your program ill-formed.
std::optional<std::reference_wrapper<record>> get_record(int key) {
if (valid(key))
return std::optional<std::reference_wrapper<record>>(lookup(key));
else
return std::nullopt;
}
Without C++17, you could just return a pointer to your record:
record* get_record(int key) {
if (valid(key))
return &lookup(key);
else
return nullptr;
}
Or if you prefer, you can keep the reference return type, and throw an exception to indicate a missing record. Though this is my least preferred approach as it makes it easy to call get_record without wrapping in a try / catch.
record& get_record(int key) {
if (valid(key))
return &lookup(key);
else
throw std::out_of_range("Invalid record key!");
}

Overload class or?

I don't understand quite well how to make class/structure which will be like this:
std::stringstream ss;
ss.str().c_str();
I would like to do something similar like this, example:
int result = MyAssignFunction(10, 5).AddParametrs();
// or
int result = MyAssignFunction(10, 5).FirstParametr;
I cannot name this thats why I cannot find any hints.
The main question is that is even possible or not? Please give me any hint.
The function MyAssignFunction needs to return an object that have an AddParametrs member function or a FirstParametr member variable.
But you need to be aware of that the object returned by MyAssignFunction in your examples is temporary. As soon as the expression is over, then it will be destructed.
A very simple example:
#include <iostream>
struct S
{
int value;
S(int initial) : value(initial) {}
S& add1() { value++; return *this; }
S& add2() { value += 2; return *this; }
S& addN(int n) { value += n; return *this; }
int get() const { return value; }
};
int main()
{
std::cout << "Operation = " << S(0).add1().add2().add1().addN(5).get() << '\n';
}
The above little dummy program will print
Operation = 9
The expression that does the "magic" is S(0).add1().add2().add1().addN(5).get(). It first creates a (temporary) object of the structure S and passes 0 to the constructor. Then on this object we call the member function add1, which returns a reference to itself, and on that reference we call add2, and so on. Finally we call the member function get to get the final value and output it.
As you can see, we can chain any number of function calls and in any order.
The above is just a very simple example, that allows the object to chain calls on itself. A member function can return another object (or reference to), and you can then chain calls to member function of that object. That is what you are seeing in your stringstream example: The str function returns a std::string object, and you call c_str on that std::string object.
If you are looking for method chaining meaning that you can reuse the output of a function call as if it were the object your function was called upon, all you need to do is return *this. Therefore your return value should be of the class type you are trying to implement method chaining on. To avoid an unnecessary invocation of the copy constructor, you should also return by reference. Here is a little example:
#include <iostream>
class A{
A& foo(){
return *this;
}
int bar(){
return 3;
}
};
int main(){
A a;
std::cout << a.foo().foo().foo().foo().bar(); //prints 3
}

rvalue reference undfefined behavior

#include<iostream>
struct Test
{
int n ;
~Test(){}
Test& operator =(int v)
{
n=v;
return *this;
}
};
Test * ptr = nullptr;
void g(Test && p)
{
std::cout << "&&";
}
void g(Test & p)
{
ptr = &p;
std::cout << "&";
}
void f(Test&& t)
{
g(t);
}
void buggy()
{
*ptr = 5;
}
int main()
{
f(Test());
buggy();
std::cin.ignore();
}
Just to be sure, the above code lead to an undefined behavior as we keep address of a tempory ?
Declaring pointer to struct Test* ptr; or "keeping address" as you call it doesn't lead to an undefined behaviour. Using pointers to objects whose lifetime has ended does.
Lifetime of the object created by Test() in main ends right after f(Test()); is executed. After that, whatever you do by using ptr is undefined. This object most likely stays in the memory even after its lifetime has ended, but you shouldn't rely on it.
You should also check out: What are all the common undefined behaviours that a C++ programmer should know about?
Yes, the temporary Test() is allocated on the stack, you take a pointer to it and it's destructor is called after returning. After that, the value of the pointer is still valid, but it points to "undefined" memory so all bets are off on dereferencing the pointer.