是否有一种干净(呃)的方式将 CRTP 与可变参数继承混合使用?

Is there a clean(er) way to mix CRTP with variadic inheritance?

最初我无法找到解决方法 this,但当我提出这个问题时,新的搜索词出现在我的脑海中,我终于找到了答案。我认为这个 post 可以作为任何有相同问题的人的重定向(因为它需要一段时间才能找到)而且我还想看看是否有任何方法可以像那样改进语法糖答案是 9 岁,当时还没有一些现代功能。该答案的代码:

#include <utility>

template <template<class> class... Mixins>
class Host : public Mixins<Host<Mixins...>>...
{
  public:
    Host(Mixins<Host>&&... args) : Mixins<Host>(std::forward<Mixins<Host>>(args))... {}
};

template <class Host> struct Mix1 {};
template <class Host> struct Mix2 {};

int main (void)
{
  typedef Host<Mix1, Mix2> TopHost;
  delete new TopHost(Mix1<TopHost>(), Mix2<TopHost>());
}

目标是消除在每个混合中使用冗余 TopHost 的需要,因为它有点烦人,而且(更重要的是)如果不小心将错误的类型用于 CRTP,可能真的会搞砸。我暗暗怀疑这可能是可能的,因为使用可变参数调用模板类型的构造函数当然是可能的(例如 new T(args...))。

理想情况下,我们可以使用如下语法:

auto mix = TopHost(Mix1(args1...), Mix2(args2...), ...);

或者甚至(以某种方式)从 using 语句中推导出 mixins:

auto mix = TopHost({args1...}, {args2...}, ...);

编辑: 所以,是的,确实,我在不知情的情况下提供了解决我自己问题的方法。第二种语法有效。如前所述,它不需要使用,因此仍然存在用户错误的可能性。 @Evg 的解决方案确实强制了这一点,虽然它更冗长,但它在技术上回答了这个问题,所以我会接受这个答案(现在)。

现在我 运行 遇到的问题是,在我的应用程序中,mixins 删除了复制构造函数,并且两种方法都创建了副本(当然,这就是原始 class 的设计方式)。那么现在问题就变成了:有没有一种不用复制就可以实现语法的方法呢?我一直在努力让这样的东西起作用,但似乎无法理解如何扩展不同大小的可变参数模板:

template < typename... > struct TypeList {};
template<typename TypeList> struct Variad;
template<typename... Types> struct Variad<TypeList<Types...>> {};

template<template<class> typename ...Args> struct Mixins;
template< class TemplList, class... Lists> struct Host;

template< template<class> class ...Components,  template<class...> class T, class... Variads>
struct Host<Mixins<Components...>, T<Variads>...> : 
  public Components<Host<Mixins<Components...>, T<Variads>...>>...
{
    Host(T<Variads>&&... args) : Components<Host<Mixins<Components...>, T<Variads>...>>(std::forward<T<Variads>...>(args))... {}
};

可以像这样使用:

int main() {
  using f1 = TypeList<int,int>;
  using f2 = TypeList<int>;
  using m1 = Mixins<Mix1, Mix2>;
  Host<m1, Variad<f1>, Variad<f2>> obj(1,2,3);
}

不一定干净,但对我来说更可取。我不太确定其中的扩展究竟是如何工作的,但我是基于我尝试创建似乎可以正常工作的嵌套变量:


#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#define get_type(x) (abi::__cxa_demangle(typeid(x).name(), NULL, NULL, NULL))

template < typename... > struct TypeList {};
template<typename TypeList> struct Variad;
template<typename... Types> struct Variad<TypeList<Types...>> {};

template<typename ...TypeListOne> struct NestedVariad;
template<template<class...> class T, typename...Types>
struct NestedVariad<T<Types>...> { };


int main() {
  using f1 = TypeList<int,int>;
  using f2 = TypeList<int>;

  NestedVariad<Variad<f1>, Variad<f2>> obj;
  cout << get_type(obj) << endl;
}

输出:NestedVariad<Variad<TypeList<int, int> >, Variad<TypeList<int> > >

但是对 mixin 使用类似的方法 class 将所有三个参数传递给每个构造函数,而不是第一个传递 2 个,第二个传递 1 个

正如 Oliv 在评论中所说,第二种语法有效:

template <class Host> struct Mix1 {
    Mix1(int, int) {}
};

template <class Host> struct Mix2 {
    Mix2(int) {}
};

using TopHost = Host<Mix1, Mix2>;
auto mix = TopHost({1, 1}, {2});

或者,您可以这样做:

template<class TopHost, class Mixes, class Tuple, std::size_t... ii>
auto make_mix_impl(std::index_sequence<ii...>, Tuple&& tuple)
{
    return TopHost(std::make_from_tuple<std::tuple_element_t<ii, Mixes>>(
        std::get<ii>(std::forward<Tuple>(tuple)))...);
}

template<template<class> class... Mixes, class... Tuples>
auto make_mix(Tuples&&... tuples)
{
    static_assert(sizeof...(Mixes) == sizeof...(Tuples));
    using TopHost = Host<Mixes...>;
    return make_mix_impl<TopHost, std::tuple<Mixes<TopHost>...>>(
        std::make_index_sequence<sizeof...(Mixes)>{}, 
        std::make_tuple(std::forward<Tuples>(tuples)...));
}

auto mix = make_mix<Mix1, Mix2>(std::make_tuple(1, 1), std::make_tuple(2));