According to cppreference member of an object which is lvalue is also an lvalue:
The following expressions are lvalue expressions:
a.m, the member of object expression, except where m is a member enumerator or a non-static member function, or where a is an rvalue and m is a non-static data member of object type;
Consider an example program with a custom class Test which is only a wrapper on int with additional prints in relevant places. In addition, there is a standalone function get which takes universal reference to T and returns its member using decltype(auto) rules.
#include <iostream>
struct Test
{
int member;
explicit Test(int member): member(member) { std::cout << "Ctor" << std::endl; }
Test(const Test& other): member(other.member) { std::cout << "Copy ctor" << std::endl; }
Test(Test&& other) noexcept: member(other.member) { std::cout << "Move ctor" << std::endl; }
Test& operator=(const Test& other) { member = other.member; std::cout << "Copy =" << std::endl; return *this; }
Test& operator=(Test&& other) { member = other.member; std::cout << "Move =" << std::endl; return *this; }
~Test() { std::cout << "Dtor" << std::endl; }
};
template <typename T>
decltype(auto) get(T&& obj)
{
std::cout << "Is obj lvalue ref? " << std::is_lvalue_reference_v<decltype(std::forward<T>(obj))> << std::endl;
return std::forward<T>(obj).member;
}
int main()
{
Test test{1234};
auto&& ret = get(test);
ret = 5678;
std::cout << "test.member is equal to " << test.member << std::endl;
return 0;
}
To my understanding decltype(auto) deduces the type of an expression as follows:
- For a
prvalueit yields its type - For an
lvalueit yields reference - For an
xvalueit yields rvalue reference
But the output of the program is as follows:
Ctor
Is obj lvalue ref? 1
test.member is equal to 1234
Dtor
Assignment ret = 5678 inside the main function does not change the value of the member inside the test object so the copy has to be done. Looks my understanding is not correct.