Deducting this в C++23

В C++23 завезут Deducting this. Если коротко, то это возможность указать this как явный параметр метода класса, при этом тип этого параметра может быть шаблоном.

Основное применение Deducting this - это устранение дублирующегося кода для одинаковых методов с разными cv-qualifiers (const/volatile) и ref-qualifiers (&, &&).

Например у нас есть следующий код на C++ 20, который идиоматически правильно определяет все варианты доступа к одной из своих переменных:

class my_class {
public:
    int & data() & { return data_; }
    const int & data() const & { return data_; }
    int && data() && { return std::move(data_); }
private:
    int data_;
};

С помощью Deducting this мы сможем заменить три одинаковых варианта функции data одним:

class my_class {
public:
    auto && data(this auto && self) {
        return std::forward<decltype(self)>(self).data_;
    }
private:
    int data_;
};

У Deducting this есть ещё несколько применений, но лично для себя считаю важным возможность замены громоздкого кода CTPR (Curiously Recurring Template Pattern) на Deducting this.

CTPR - это способ реализации полиморфизма через наследование, аналогичный виртуальным функциям, но только времени компиляции, а не исполнения.

Например, у нас есть базовый класс base, в котором есть метод foo. Мы хотим сделать так, чтобы метод foo вызывал метод bar, определённый в классах-наследниках, но без динамического диспатчинга. Т. е. мы не планируем вызывать метод foo у переменной типа base с неизвестным во время компиляции конкретным типом. Метод foo будет вызываться всегда только у переменной, имеющий тип класса-наследника, известный во время компиляции. Сделать это можно так:

template <typename Derived>
class base {
public:
    void foo() {
        static_cast<Derived*>(this)->bar();
    }   
};

class derived: public base<derived> {
public:
    void bar() {
        // ...
    }
};

// где-то дальше в коде
derived d;
d.foo();

Такой подход используется уже десятилетиями, но выглядит довольно убого. После добавления Deducting this больше не потребуется городить шаблоны и использовать static_cast:

class base {
public:
    void foo(this auto && self) {
        self.bar();
    }   
};

class derived {
public:
    void bar() {
        // ...
    }
};

У Deducting this есть ещё несколько разных применений: передача this по значению, использование в лямбдах, исправление мелких проблем при overload resolution.

Ссылка на Proposal: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html

comments powered by Disqus