Why does std::declval<int&>() returns lvalue reference?

Below the return value is “is lvalue”. However, in the documentation, (https://www.cplusplus.com/reference/utility/declval/), it seems that we always get rvalue reference from std::declval. why?

1
2
3
if (std::is_lvalue_reference<decltype(std::declval<int>())>::value) cout<<"is lvalue ref"<<endl;
            else cout<<"not lvalue ref"<<endl;



Last edited on
The URL parser here fooked up your link. Put a space before at the end of a link with trailing characters so when someone clicks the link it isn't a 404.

You can also use BB tags instead of the space(s) (https://www.cplusplus.com/reference/utility/declval/). I use empty opening and closing teletype tags after the link and before the closing ).
Below the return value is “is lvalue”

No, the type decltype(std::declval<int>()) is exactly int&& as evidenced by this program:
1
2
3
#include <type_traits>
#include <utility>
static_assert(std::is_same_v<decltype(std::declval<int>()), int&&>);

https://coliru.stacked-crooked.com/a/f33f7ea5775c936c

It seems that we always get rvalue reference from std::declval. why?

Not true, if you specify an lvalue reference type as a template argument.

To give a concrete example, here is the signature for std::declval:
1
2
template <class T>
  typename add_rvalue_reference<T>::type declval() noexcept;

As the template shows, the return type of std::declval<int&> is typename add_rvalue_reference<int&>::type, which is an lvalue reference type as shown below:

1
2
3
4
5
6
7
8
9
10
11
12
#include <type_traits>
#include <utility>

static_assert(std::is_same_v<std::add_rvalue_reference_t<int>, int&&>);
static_assert(std::is_same_v<std::add_rvalue_reference_t<int&>, int&>); // <--
static_assert(std::is_same_v<std::add_rvalue_reference_t<int&&>, int&&>);
using lvalue_ref = int&;
using rvalue_ref = int&&;
static_assert(std::is_same_v<lvalue_ref&, int&>);
static_assert(std::is_same_v<lvalue_ref&&, int&>);
static_assert(std::is_same_v<rvalue_ref&, int&>);
static_assert(std::is_same_v<rvalue_ref&&, int&&>);

These are examples of reference collapsing. Reference collapsing was introduced in C++11 to support perfect forwarding.

One of these days I'm going to write a tutorial for this. We get a slow but steady stream of questions about this topic, and they're not easy.
Last edited on
Scott Myers has a whole section on reference collapsing in his book "Effective Modern C++" - Item 28. The while of part 5 deals with move/forward/universal references etc.

Topic archived. No new replies allowed.