Я пытаюсь создать универсальный класс, который принимает набор типов, сохраняет их в кортеже и может применять к ним функцию.
Что я пробовал до сих пор, так это следующее:
#include <tuple>
struct Base{
virtual void base_function() = 0;
};
template<typename ...T>
struct A : public Base{
std::tuple<T...> as;
A(T... pack):as(pack...){};
void base_function(){
std::apply([](auto t){t.base_function();}, as);
}
};
struct B : public Base{
void base_function(){};
};
struct C : public Base{
void base_function(){};
};
struct D : A<B, C>{
D():A(B(),C()){};
};
Я ожидал, что apply будет вызываться для base_function из классов B и C при вызове base_function для D. Но компилятор выдает следующую ошибку:
error: no matching function for call to
'__invoke(A<T>::base_function() [with T = {B, C}]::<lambda(auto:1)>, std::__tuple_element_t<0, std::tuple<B, C> >&, std::__tuple_element_t<1, std::tuple<B, C> >&)'
@Spencer Удаление класса D не вызывает ошибку, потому что компилятор не генерирует никакого кода без объявления версии A. Базовый класс был там, когда код сокращался, и он должен дать некоторый контекст.
Но вы могли бы просто объявить A<B,C> везде, где объявили объект D.





std::apply не делает того, что вы думаете. Это для передачи кортежа параметров в функцию (тип Callable). Другими словами, в самом кортеже нет функции base_function. см. https://en.cppreference.com/w/cpp/utility/apply
Я бы интерпретировал замешательство OP как мысль, что apply берет унарную функцию и применяет ее к каждому элементу в кортеже по очереди, а не как применение унарной функции непосредственно к кортежу (что было бы бессмысленно, поскольку вы могли бы просто... сделать это уже).
Первый параметр std::apply должен быть функтором с той же арностью, что и количество элементов кортежа, поэтому в вашем случае вариативность:
template <typename ...Ts>
struct A : public Base{
std::tuple<Ts...> as;
A(Ts... pack) : as(pack...){}
void base_function(){
std::apply([](auto&... ts){(ts.base_function(), ...);}, as);
}
};
Никогда не видел синтаксиса, используемого в лямбде, не могли бы вы объяснить немного больше, что он на самом деле делает. Как компилятор узнает, что делать с пакетом параметров ts?
Посмотрите на складное выражение.
Это здорово, но вы можете добавить объяснение, почему std::apply не работает так, как думал OP.
Просто чтобы вы знали, классы
BaseиD(и любое наследование отBase) не нужны для минимального примера. Ваша проблема проявляется так же (и решение @ Jarod42 работает так же) даже без них.