#опытным
Наследованием в языке С++ никого не удивить. Все постоянно его видят в коде и используют. Но что, если я вам скажу, что вы можете наследоваться от лямбды! Как? Давайте разбираться.
Как это вообще возможно?
Ну это не удивительно для тех, кто знает, чем на самом деле являются лямбды. Это по сути своей классы, чей тип знает только сам компилятор, с перегруженным оператором(). То, что ее называют безымянной функцией, это не совсем правда. Все у нее есть.
То есть это класс, у которого есть вполне конкретный метод и даже поля(тут зависит от того, что захватила лямбда).
Значит это вполне легальный кандидат на наследование!
Придется конечно немного поколдовать вокруг отсутствия имени, но для С++ это не проблема.
template<class Lambda>
struct DerivedFromLambda : public Lambda
{
DerivedFromLambda(Lambda lambda) : Lambda(std::move(lambda)) {}
using Lambda::operator();
};
int main(){
auto lambda = []{return 42;};
DerivedFromLambda child{lambda};
std::cout << child() << std::endl;
}
// OUTPUT:
// 42
Ничего особенного. Мы просто создали класс-обертку над каким-то функциональным объектом и используем его оператор(), как свой.
Дальше создаем лямбду и создаем объект обертки, просто передавая лямбду в конструктор. Мы специально не указываем явно шаблонный параметр DerivedFromLambda, потому что мы не знаем настоящего имени лямбды. Мы даем возможность компилятору самому вывести нужный шаблонный тип на основании инициализатора. Это возможно благодаря фиче С++17 Class Template Argument Deduction.
Но даже и на С++11-14 можно написать подобное. Ведь у нас есть оператор decltype, который возвращает в точности тип того выражения, которое мы в него передали. Тогда мы бы создавали объект так:
auto lambda = []{return 42;};
DerivedFromLambda<decltype(lambda)> child{lambda};Зачем это нужно только? К этому мы будем потихоньку подбираться следующие пару постов.
Do surprising things. Stay cool.
#template #cppcore #cpp11 #cpp17