Home > Enterprise >  Should c 23's "deducting this" feature be used with template function?
Should c 23's "deducting this" feature be used with template function?

Time:11-05

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?

godbolt

// 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.

  • Related