I was reading https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/ specifically this piece
class cat {
...
//After
template <class Self>
auto&& get_held_toy(this Self&& self) {
return self.held_toy_;
}
//After forwarding
template <class Self>
auto&& get_held_toy(this Self&& self) {
return std::forward<Self>(self).held_toy_;
}
};
I tried the following code, it still compiles. Is there any specific reason to use template function in Microsoft blog's example?
// turn on c 23
class cat {
...
//After forwarding
auto&& get_held_toy(this cat&& self) {
return std::forward<Self>(self).held_toy_;
}
};
CodePudding user response:
With this declaration:
auto&& get_held_toy(this cat&& self) {
The explicit object parameter always has type cat&&
(that is, an rvalue reference), so you could only call this function on rvalues:
auto c = cat{};
// c.get_held_toy(); // error
std::move(c).get_held_toy();
cat{}.get_held_toy();
It's basically the same as:
auto&& get_held_toy() && {
Which there is use for, but that isn't "deducing this
" (it's just an explicit object parameter).
The real magic of:
template <class Self>
auto&& get_held_toy(this Self&& self) {
// Or
auto&& get_held_toy(this auto&& self) {
Is that the type of the object parameter, what used to be "*this
", can be deduced (hence "deducing this
") from how it is being called:
auto c = cat{};
toy_type& x = c.get_held_toy(); // mutable lvalue
const toy_type& y = std::as_const(c).get_held_toy(); // const lvalue
toy_type&& z = std::move(c).get_held_toy(); // xvalue
Without having to write 3 different overloads.
CodePudding user response:
First, this Self
allows to determine the actual qualifier of object it was called for. One definition for all possible overloads without possiblity to cause ambigous declaration. For template function &&
is a forwarding reference. If you remove template, you limit this function to xvalue\prvalue ones. It allows this type of recurrent behaviour:
class cat {
protected:
ball held_toy_;
public:
//After forwarding
template <class Self>
auto&& get_held_toy(this Self&& self) {
return std::forward<Self>(self).held_toy_;
}
};
class displacer : public cat
{
protected:
player held_toy_;
friend class cat;
};
int main()
{
cat mew;
displacer boo;
std::cout << typeid(mew.get_held_toy()).name() << std::endl;
std::cout << typeid(cat().get_held_toy()).name() << std::endl;
std::cout << typeid(boo.get_held_toy()).name() << std::endl; // displacer::held_toy_
}
Second, your way to define it is ill-formed in case of std::forward<Self>(self).held_toy_;
- Self
in not cat
or displacer
, substitution for std::forward
fails.