Binding an rvalue to cout

I am seeking a general-purpose way to bind an rvalue (such as a function return value) to cout.

Eg: Consider the following class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct A
{
    int x;

    A(const int& rx) :
        x {rx}
    {
    }
};


/// O/P operator for class A
/// Accepts an lvalue reference to A.
ostream& operator<<(ostream& os, A& a)
{
   os << a.x;

   return os;
}


/// A instances
A a1 {1}, a5 {5}, a10 {10};


Now, I can send lvalues (such as the above A instances) to cout:

1
2
3
4
5
6
void testlvals()
{
    cout << "a1 = " << a1 << endl;
    cout << "a5 = " << a5 << endl;
    cout << "a10 = " << a10 << endl;
}



However, consider the following function which returns an A instance and an attempt to print the rvalue:

1
2
3
4
5
6
7
8
9
A f()
{
    return A {0};
}

void testrvals()
{
    cout << "f() = " << f() << endl;
}


This will give the following compilation error, which basically indicates we can't bind an rvalue (the function return value) to an lvalue (ostream& os).


error: cannot bind 'std::basic_ostream<char>' lvalue to 'std::basic_ostream<char>&&'|
error:  initializing argument 1 of 'std::basic_ostream<_CharT, _Traits>& std::operator<<(std::basic_ostream<_CharT, _Traits>&&, const _Tp&) [with _CharT = char; _Traits = std::char_traits<char>; _Tp = A]'|


One way to solve this problem is to overload the output operator << to accept an rvalue reference to class A:

1
2
3
4
5
6
7
/// Accepts an rvalue reference to A.
ostream& operator<<(ostream& os, A&& a)
{
   os << a.x;

   return os;
}



This technique requires us to provide 2 overloads for every type that we want to output : one that accepts an lvalue, another that accepts an rvalue.

I'd like to know if there is a simpler method without such overhead.

Thanks.
The << operator should accept a const reference.

 
ostream& operator<<(ostream& os, const A& a)

Edit: Sorry that didn't work. I didn't have it right :+(
Last edited on
¿and how would that auto be resolved?
pass A by const ref to << overload:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <string>

struct A
{
    int x;

    A(const int& rx) : x {rx} { }
};

std::ostream& operator << (std:: ostream& os, const A& a)
{
   os << a.x;
   return os;
}

A f()
{
    return A {0};
}

void testrvals()
{
    std::cout << "f() = " <<  f() << "\n";
}

int main()
{
   A a1 {1}, a5 {5}, a10 {10};
   testrvals();
}


Last edited on
OP: I wanted to clarify something - the reason your original code was not working did not have anything to do with const'ness of the A parameter, rather it was due to the fact that the function signatures of the operator overload did not match the return value of f() on which << is applied within the definition of f(). This can be resolved in various ways:
(a) pass A by value to overload operator << method, keep f() unchanged:
http://coliru.stacked-crooked.com/a/e3fc1676f01ef043
(b) pass A by reference (i.e. unchanged from the OP) to overload operator <<, return f() by reference:
using r-value reference (C++11):
http://coliru.stacked-crooked.com/a/9369f1f053e5a0f5
Note that you'll get a compiler warning in this case though that function f() returns address of local variable. As we're returning f() within testrvals() which returns void this is OK for illustration purposes but if you had further uses for the return value of f() you could in turn capture it with move semantics:
1
2
A b {std::move(f())};
   std::cout << b.x << "\n";

(c) keep << and f() unchanged but add lvalue reference to f()'s return within testrvals():
http://coliru.stacked-crooked.com/a/dd1cb05c256c9e0d
... if you had further uses for the return value of f() you could in turn capture it with move semantics:
1
2
A b {std::move(f())};
std::cout << b.x << "\n";

The move is redundant, here, because the expression f() is an rvalue.
[Edit: I'm wrong -- in this case, f() returns a reference; the move would move from the referent instead of a copy of the referent, except...:]

More importantly, the referent no longer exists, so that snippet has undefined behavior.
See the first bullet point:
http://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary

Edit:
(The problem is in your second link, lines 17-27, although you don't move from the result, the problem is the use of a object whose lifetime has ended, on line 26).
Last edited on
Max - you misunderstood (or perhaps I was not clear enough?):
but if you had further uses for the return value of f()
... the 'but' is the key here meaning if OP was interested in using the return value of f() here is how to go about it using move semantics etc ... so object b of type A is initialized with std::move(f()) and that is why std::cout << b.x << "\n" works. The entire program would then look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <utility>

struct A
{
    int x;

    A(const int& rx) : x {rx} { }
};

A& f()
{
    A&& y =  std::move(A (12));

    return y;//f() still returns local variable 
}

int main()
{
  A b{std::move(f())};
  std::cout << b.x << "\n";//output 12
}
The program has undefined behaviour.

It is easy to verify that:
object b of type A is initialized with std::move(f()) and that is why std::cout << b.x << "\n" works.
The initialisation of object b of type A engenders undefined behaviour because it attempts to access (move from) an object after its life-time has ended (after it has been destroyed).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <utility>

struct A
{
    int x;

    A( int rx ) : x {rx} {}

    A( A&& that ) : x {that.x}
    { std::cout << "move from object at " << std::addressof(that) << '\n' ; }

    ~A()
    { x = 0xcccccccc ; std::cout << "**destroy object at " << this << '\n' ; }
};

A& f()
{
    A&& y =  std::move(A (12));

    return y;
}

int main()
{
  A b{std::move(f())};
  std::cout << b.x << "\n";
}

http://coliru.stacked-crooked.com/a/66aab2198db3c431
http://rextester.com/XYHD25676
big blooper, of course f() is returning by reference. max - you're quite right
> big blooper, of course f() is returning by reference.

Even if f() is modified to return a prvalue, the incorrect use of std::move will engender undefined behaviour.

1
2
3
4
5
6
A f()
{
    A&& y =  std::move(A (12));

    return y; // undefined behaviour, the object referred to by y has already been destroyed
}

**destroy object at 0x7ffc2b43b5b8
copy from object at 0x7ffc2b43b5b8
...

http://coliru.stacked-crooked.com/a/6fa435728be426a8

This is better; potentially inefficient, but at least eschews the undefined behaviour:
1
2
3
4
5
6
A f()
{
    A&& y = A(12) ;

    return y ;
}

http://coliru.stacked-crooked.com/a/6b597a2c548b63d2

Remove the gratuitous std::move() in main() and the code is potentially more efficient:
1
2
3
4
5
6
7
8
9
10
11
12
A f()
{
    A&& y = A(12) ;

    return y ;
}

int main()
{
  A b{ f() };
  std::cout << b.x << "\n";
}

http://coliru.stacked-crooked.com/a/0407ade1abb99caf

The simple, sane way to write the code (without disabling copy elision) is:
1
2
3
4
5
6
7
8
9
10
11
12
A f()
{
    // A&& y = A(12) ;

    return A(12) ;
}

int main()
{
  A b{ f() };
  std::cout << b.x << "\n";
}

http://coliru.stacked-crooked.com/a/6c320b5161b1156d
Last edited on
Yes, declaring the output operator << to take a const argument works for both lvalues (const and non-const) and rvalues.

1
2
3
4
5
6
7
8
9
/// O/P operator for class A
/// Accepts lvalue references (const and non-const)
/// and rvalue references to A.
ostream& operator<<(ostream& os, const A& a)
{
   os << a.x;

   return os;
}


Now, we can test the output operator << with both lvalue references (const and non-const) and rvalue references:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// A instances
A a1 {1}, a5 {5}, a10 {10}, const ca9 {9};;

void testlvals()
{
    cout << "a1 = " << a1 << endl;
    cout << "a5 = " << a5 << endl;
    cout << "a10 = " << a10 << endl;

    cout << "a9 = " << ca9 << endl;	/// const
}

A f()
{
    return A {0};
}

void testrvals()
{
    cout << "f() = " << f() << endl;
}



In seeking an explanation for why this works, I found the following in Stroustrup's TC++PL4:

"An rvalue reference can bind to an rvalue, but not to an lvalue. In this, an rvalue reference is exactly opposite to an lvalue reference.

[However,] an rvalue can bind to both an rvalue reference and a const lvalue reference."

Basically, an rvalue is a temporary and soon to disappear. If it were possible to assign an rvalue to an lvalue, it would be possible to modify the rvalue. However, the modified rvalue would then shortly disappear. Therefore the compiler prevents one from assigning an rvalue to an lvalue.

On the other hand, when an lvalue is declared const, it can't be modified; therefore even if an an rvalue were assigned to that lvalue, the rvalue can't be modified thru the lvalue. Therefore, the compiler can allow such an assignment.

This is another reason that justifies the importance of "const correctness".

Basically, a function should take only const parameters, unless the function needs to modify the argument.
Further, it is a best practice that a function shouldn't modify arguments at all. If an argument is to be modified, it should be by the function's return value. This is from a readability / understandability viewpoint.

Keeping these 2 points in mind, we may formulate a best practice as :

"A FUNCTION SHOULD ALWAYS BE DECLARED TO TAKE ONLY const PARAMETERS."

The only exception is where the function takes a parameter that it intends to modify and then return as the return value.
An excellent example in fact is the output operator itself, which takes a non-const ostream& reference which it modifies and then returns:

1
2
3
4
5
6
7
8
9
/// O/P operator for class A
/// Accepts lvalue references (const and non-const)
/// and rvalue references to A.
ostream& operator<<(ostream& os, const A& a)
{
   os << a.x;

   return os;
}


Following are the benefits when a function parameter is declared const:
1) The compiler ensures the function can't modify the argument.
2) The programmer understands immediately that the function can't modify the argument.
3) non-const lvalues can be passed to the parameter.
4) const lvalues can be passed to the parameter.
5) rvalues can be passed to the parameter. All rvalues are non-const. There is no such thing as a const rvalue, since an rvalue permits a "destructive read".

Naturally, the same treatment also applies to constructors.
Yes, accepting a constant reference parameter is the most correct solution. This avoids at least one copy or move, enforces const-correctness, and binds objects irrespective of value category (although it obviously won't bind a volatile A).

You might also have considered accepting a forwarding reference (e.g., T&& in the context of a deduced T), for the same reason that it will bind values of any value category. The problem with that is that the additional overload creates ambiguities... this, I think, was what @TheIdeasMan was onto:

1
2
3
4
5
6
7
8
9
10
11
# include <iostream>
struct Foo { int x; };
template <typename T>
std::ostream& operator<<(std::ostream& s, T&& obj) { 
  return s << std::forward<T>(obj).x;
}

int main() {
  s << Foo{42}; // works
  // s << "\n";  // but this is ambiguous
}

http://coliru.stacked-crooked.com/a/41a3326ae91819ea

I would also amend:
The only exception is where the function takes a parameter that it intends to modify and then return as the return value.

to
The only exception is where the function takes a parameter that it intends to modify
; Returning the modified value is only done in this case to support the method chaining syntax stream << a << b. Sometimes so-called "output parameters" can be useful even (especially) when the modified value is not also returned from a function.
Last edited on
Topic archived. No new replies allowed.