3

I've found a trick to use optional reference in standard C++11. Do you think that this technique is reliable and the behaviour is well defined according to the the C++ standard ?

// Optional reference using C++11 
// V. Reverdy - 2013
#include <iostream>
#include <type_traits>

template <class T = int, class = typename std::enable_if<std::is_same<typename std::decay<T>::type, int>::value>::type>
void f(T&& n = T())
{
    std::cout<<"--------"<<std::endl;
    std::cout<<"is const = "<<std::is_const<T>::value<<std::endl;
    std::cout<<"is reference = "<<std::is_reference<T>::value<<std::endl;
    std::cout<<"is lvalue reference = "<<std::is_lvalue_reference<T>::value<<std::endl;
    std::cout<<"is rvalue reference = "<<std::is_rvalue_reference<T>::value<<std::endl;
    std::cout<<"--------"<<std::endl;
    n *= 2;
} 

int main(int argc, char* argv[])
{
    int n = 42;
    std::cout<<"n = "<<n<<std::endl;
    f();
    std::cout<<"n = "<<n<<std::endl;
    f(n);
    std::cout<<"n = "<<n<<std::endl;
    return 0;
}

The result is :

n = 42
--------
is const = 0
is reference = 0
is lvalue reference = 0
is rvalue reference = 0
--------
n = 42
--------
is const = 0
is reference = 1
is lvalue reference = 1
is rvalue reference = 0
--------
n = 84

It seems to work on all compilers available on liveworkspace : LWS

Vincent
  • 57,703
  • 61
  • 205
  • 388
  • Do you have a real-world use-case for this? – Mat Mar 13 '13 at 13:38
  • 1
    The interesting part is that type type `T` varies between `int`, `int&`, `int&&` and `int const&`, with the `int` choice happening if you don't pass in anything. The reason why we get `int` with no parameters is because the default value for the template argument is used, which means `T` is no longer deduced, and `T&&` is no longer being used in a type deduction context... Try `std::is_reference::value` in that function -- the downside of your technique is that `T` is sometimes in a deduction context, and sometimes not. – Yakk - Adam Nevraumont Mar 13 '13 at 14:31

2 Answers2

2

Wat.

Firstly, what you have "achieved" could be far simpler achieved by, say, overloading. And secondly, it is not the same as an optional reference at all. The optional reference is a value, which may or may not contain a reference at run-time. The behaviour is well-defined but it's neither desirable nor an optional reference. Binding references to temporaries as default arguments is nice in some situations but it's a billion miles from an optional reference.

Puppy
  • 144,682
  • 38
  • 256
  • 465
1

It is well-defined, yes, because "universal references" (mind the fact that this is a non-standard term) can resolve into rvalue references and therefore bind to temporaries, but you're not creating an "optional reference".

References must always be initialized and must always be bound to an object, they cannot be "bound or not bound" (which is my intuitive understanding of what you mean by "optional reference").

Here, you are simply binding that reference to a temporary. What you did is equivalent to the following:

template<typename T> void f(T&& t) { ... }
void f() { f(int()); }

If you meant to say the you are now able to create a function which accepts a reference and may be invoked without providing any argument - which is IMO a more correct way to put it - then this is true, but I wouldn't consider it a breaking discovery.

Even in C++03 you could do something similar, although for references to const only:

template<typename T>
void f(T const& = T()) { ... }

Since C++11 introduced non-const references which can bind to temporaries (rvalue references), a natural generalization follows, which is what your technique exploits.

Andy Prowl
  • 124,023
  • 23
  • 387
  • 451