Sorry but I don't get why/when to use auto or decltype(auto) for return types

Hi,

I am confused with when and why to use auto or decltype(auto) on the return types of functions.
I thought auto by itself always made a copy of the stuff at its right side. I thought auto& returns an lreference to the stuff at its right and auto&& returned an rreference to the stuf at its right.

But this code leaves me perplexed:

1
2
3
4
Customer c{ "Tim", "Stark", 42 };
auto [f, l, a] = c;

auto [ff, ll, aa] = getCustomer();


since f,ff,l and ll have type std::string&& while a and aa have type int&!!


To be complete, I am including the relevant code beneath this in case it helps figure out what is going on:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
struct Customer
{
	void set_name(const std::string& name)
	{
		this->name = name;
	}

	void set_last(const std::string& last)
	{
		this->last = last;
	}

	void set_age(int age)
	{
		this->age = age;
	}

	[[nodiscard]] const std::string& get_name() const
	{
		return name;
	}


	[[nodiscard]] const std::string& get_last() const
	{
		return last;
	}


	[[nodiscard]] int get_age() const
	{
		return age;
	}

	[[nodiscard]] std::string& get_name()
	{
		return name;
	}

	[[nodiscard]] std::string& get_last()
	{
		return last;
	}

	[[nodiscard]] int& get_age()
	{
		return age;
	}

	Customer(std::string name, std::string last, int age)
		: name(std::move(name)),
		last(std::move(last)),
		age(age)
	{
	}
private:
	std::string name;
	std::string last;
	int age;
};



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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
template<typename T>
struct ::std::tuple_size
{

};

template<>
struct ::std::tuple_size<Customer>
{
	static constexpr int value = 3;
};

template<size_t Idx, typename Type >
struct std::tuple_element
{
};

template<size_t Idx>
struct std::tuple_element<Idx, Customer>
{
	using type = std::string;
};

template<>
struct std::tuple_element<2, Customer>
{
	using type = int;
};

template<std::size_t I> decltype(auto) get(Customer& c)
{
	static_assert(I < 3);

	if constexpr (I == 0)
	{
		return c.get_name();
	}
	else if constexpr (I == 1)
	{
		return c.get_last();
	}
	else
		return c.get_age();
}

template<std::size_t I> decltype(auto) get(const Customer& c)
{
	static_assert(I < 3);

	if constexpr (I == 0)
	{
		return c.get_name();
	}
	else if constexpr (I == 1)
	{
		return c.get_last();
	}
	else
		return c.get_age();
}

template<std::size_t I> decltype(auto) get(Customer&& c)
{
	static_assert(I < 3);

	if constexpr (I == 0)
	{
		return std::move(c.get_name());
	}
	else if constexpr (I == 1)
	{
		return std::move(c.get_last());
	}
	else
		return c.get_age();
}


Customer getCustomer()
{
	return Customer{ "Juan","Dent", 60 };
}



What is going on here??

Regards,
Juan

No responses? Was my question too vague or general?

I thought auto& returns an lreference to the stuff at its right and auto&& returned an rreference to the stuf at its right.


Simply:

But here, you have to specify the & or && along with auto. What if you're coding a perfect forwarding function and you don't know the arguments? This is where decltype(auto) comes in which was introduced with C++14

For info about decltype() see https://en.cppreference.com/w/cpp/language/decltype. This gives the required type based on the specified expression. But if the expression is to be determined, then decltype(auto) is used.

auto follows the template argument deduction rules and is always an object type. decltype(auto) follows the decltype rules for deducing reference types based on value categories.

See also http://www.cplusplus.com/forum/general/188645/
Hello JUANDENT,

No your question is not too vague or general. I think it is a good question, I am not the right person to answer it.

Sometimes it may take a day or 2 to see a responce. I have seen some take even longer. Other times it may just take bumping the post to the top of the stack before someone notices.

As I look over your code I have to ask what C++ standard is it written for. Some of the code is beyond what I know and I will have to research "decltype(auto)".

Andy
This is a "special case" of auto:

1
2
3
4
Given:
Customer c{ "Tim", "Stark", 42 };

auto [f, l, a] = c;


In this situation the compiler is being asked to provide types from withing a Customer for the various members. It is a simplification of something like:

1
2
3
auto f = c.name;
auto l = c.last;
auto a = c.age;


It is also used with tuples (and I vaguely remember reading a discussion where the inspiration came from the syntax for getting the various entries from a tuple).



What you are wondering about is C++17's structured binding.
https://en.cppreference.com/w/cpp/language/structured_binding

A similar language feature was made available in C++11 with std::tie that only worked on tuples.
https://en.cppreference.com/w/cpp/utility/tuple/tie
Registered users can post here. Sign in or register to post.