Это было удивительно сложно. Следующий код позволяет наследовать от произвольного списка типов, и каждый тип списка будет использоваться в качестве базового ровно один раз:
#include <type_traits>
template<class T, class... Ts>
struct is_among;
template<class T, class... Ts>
struct is_among<T, T, Ts...> : std::true_type {
using value_t = T;
};
template<class T, class U, class... Ts>
struct is_among<T, U, Ts...> : is_among<T, Ts...> {};
template<class T>
struct is_among<T> : std::false_type{};
template<class SFINAE, class... Ts>
struct inherit_once;
template<class T, class... Ts>
struct inherit_once<std::enable_if_t<!is_among<T, Ts...>::value>,T, Ts...> : inherit_once<void, Ts...>, T {};
template<class T, class... Ts>
struct inherit_once< std::enable_if_t<is_among<T, Ts...>::value>, T, Ts...> : inherit_once<void, Ts...> {};
template<>
struct inherit_once<void>{};
struct A{
void operator()(int){}
};
struct B : inherit_once<void, A, A> {
int i;
};
int main(){
static_assert(sizeof(int) == sizeof(B));
B b;
b(1);
}
Редактировать: с несколькими экземплярами:
#include <utility>
template<class F, size_t id>
struct id_wrap: F {};
template<class id_seq, class... Funcs>
struct OptHelper;
template<class... Funcs, size_t... ids>
struct OptHelper<std::index_sequence<ids...>, Funcs...> : id_wrap<Funcs, ids>... {};
template<class... Funcs>
struct Opt: OptHelper<std::index_sequence_for<Funcs...>, Funcs...> {int i;};
struct Functor {
void operator()(int) {}
};
int main(int argc, char *argv[])
{
static_assert(sizeof(Opt<Functor,Functor>) == sizeof(int));
Opt<Functor> f{};
f(1);
}