有没有一种干净(更)的方法将 CRTP 与可变参数继承混合在一起?
Is there a clean(er) way to mix CRTP with variadic inheritance?
最初我找不到一种方法来做到这一点,但是当我提出这个问题时,新的搜索词浮现在脑海中,我终于找到了答案。我认为这篇文章既可以作为任何有相同问题的人的重定向(因为花了一段时间才找到),我也想看看是否有任何方法可以稍微改进语法糖,因为这个答案已经 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 一起使用,可能会真正搞砸一些东西。我偷偷地怀疑这可能是可能的,因为使用可变参数调用模板化类型的构造函数当然是可能的(例如,新的 T(args...
理想情况下,我们将能够使用以下语法:
auto mix = TopHost(Mix1(args1...), Mix2(args2...), ...);
甚至(以某种方式)从 using 语句中推断出混合:
auto mix = TopHost({args1...}, {args2...}, ...);
编辑: 所以,是的,确实,我在不知道的情况下提供了我自己问题的解决方案。第二种语法有效。如前所述,它不需要使用,因此仍然存在用户错误的可能性。@Evg的解决方案确实强制了这一点,尽管它在技术上更冗长,但它在技术上回答了这个问题,所以我(现在)会接受这个答案。
现在我遇到的问题是,在我的应用程序中,mixins 删除了复制构造器,并且两种方法都创建了副本(当然,这就是原始类的设计方式)。所以现在的问题变成了:有没有办法在不复制的情况下实现语法?我一直在尝试让这样的东西工作,但似乎无法理解如何扩展不同大小的可变参数模板:
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 类使用类似的方法将所有三个参数传递给每个构造函数,而不是将 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));
- 在不传递参数数量且只有3个点的情况下,如何使用变差函数
- 如何使用可变参数模板强制转换每个变体类型
- 关于如何在具有单个参数的变体构造中选择替代方案?
- 调用参数排列不变函数 f(i++, i++)
- 参数归纳与标准::变体
- 模板化回调参数的逆变,如 C# 中的逆变
- 如何在没有参数包的情况下编写变差函数
- 通过具有嵌套类的工厂类获取多个变异类模板参数包
- 获取模板参数的成员变量值列表
- 保留短 lambda 用作函数的中间参数,使用 clang 格式保持不变
- 如何定义变体<x,y,z>提取模板参数的子类型
- 正确对齐内存模板,参数顺序不变
- 递归中不同参数类型的变元模板函数
- 通过函数指针传递给变差函数的参数会更改其值
- 提升预定义为带有参数的全局 lambda 的变体访问者
- 使用可变参数模板参数提升变体访问者
- boost ::变体 - 为什么模板参数比const字符串参数具有更高的优先级
- 将变参数包中的值加载到临时数组中
- 使用额外参数提升变体访客
- 从变长参数列表中提取std::string