在C++中,是否可以将模板与其参数区分开来
Is it possible to disentangle a template from its arguments in C++?
假设我收到一个模板的两个参数,T1和T2。如果我知道T1本身是一个模板化的类(例如,容器),而T2可以是任何东西,那么我有可能确定T1的基本模板类型并使用T2作为其参数来重建它吗?
例如,如果我收到std::vector<int>
和std::string
,我将希望自动构建std::vector<std::string>
。然而,如果给我std::set<bool>
和double
,它会产生std::set<double>
。
在回顾了type_traits、相关博客和其他问题之后,我看不出解决这个问题的一般方法。我目前能看到的完成这项任务的唯一方法是为每种类型构建模板适配器,这些适配器可以作为T1传入。
例如,如果我有:
template<typename T_inner, typename T_new>
std::list<T_new> AdaptTemplate(std::list<T_inner>, T_new);
template<typename T_inner, typename T_new>
std::set<T_new> AdaptTemplate(std::set<T_inner>, T_new);
template<typename T_inner, typename T_new>
std::vector<T_new> AdaptTemplate(std::vector<T_inner>, T_new);
我应该能够使用decltype并依靠运算符重载来解决我的问题。大致如下:
template <typename T1, typename T2>
void MyTemplatedFunction() {
using my_type = decltype(AdaptTemplate(T1(),T2()));
}
我是不是错过了什么?有更好的方法吗?
我为什么要这样做
我正在构建一个C++库,在那里我想简化用户构建模块化模板所需的操作。例如,如果用户想要构建基于代理的模拟,他们可能会配置一个具有生物体类型、种群管理器、环境管理器和系统学管理器的World模板。
每个管理者还需要知道生物体的类型,因此声明可能看起来像:
World< NeuralNetworkAgent, EAPop<NeuralNetworkAgent>,
MazeEnvironment<NeuralNetworkAgent>,
LineageTracker<NeuralNetworkAgent> > world;
我更希望用户不必每次都重复NeuralNetworkAgent
。如果我能够更改模板参数,那么可以使用默认参数,并且以上内容可以简化为:
World< NeuralNetworkAgent, EAPop<>, MazeEnvironment<>, LineageTracker<> > world;
此外,从一个世界类型转换到另一个世界更容易,而不用担心类型错误。
当然,我可以使用static_assert处理大多数错误,只处理较长的声明,但我想知道是否有更好的解决方案。
这似乎以您所询问的方式工作,用gcc 5.3.1.进行了测试
#include <vector>
#include <string>
template<typename T, typename ...U> class AdaptTemplateHelper;
template<template <typename...> class T, typename ...V, typename ...U>
class AdaptTemplateHelper<T<V...>, U...> {
public:
typedef T<U...> type;
};
template<typename T, typename ...U>
using AdaptTemplate=typename AdaptTemplateHelper<T, U...>::type;
void foo(const std::vector<std::string> &s)
{
}
int main()
{
AdaptTemplate<std::vector<int>, std::string> bar;
bar.push_back("AdaptTemplate");
foo(bar);
return 0;
}
本周最佳C++问题。
这基本上是两个独立的问题:如何将类模板的实例化分解为类模板,然后如何获取类模板并实例化它。让我们遵循这样的原则:如果所有东西都是类型,那么模板元编程更容易。
第一部分,第二部分。给定一个类模板,让我们把它变成一个元函数类:
template <template <typename...> class F>
struct quote {
template <typename... Args>
using apply = F<Args...>;
};
这里,quote<std::vector>
是一个元函数类。它是一个具有成员模板apply
的具体类型。所以quote<std::vector>::apply<int>
给了你std::vector<int>
。
现在,我们需要解压缩一个类型。让我们称之为unquote
(至少对我来说这似乎很合适)。这是一个接受类型并生成元函数类的元函数:
template <class >
struct unquote;
template <class T>
using unquote_t = typename unquote<T>::type;
template <template <typename...> class F, typename... Args>
struct unquote<F<Args...>> {
using type = quote<F>;
};
现在,您所需要做的就是将实例化传递到unquote
中,并向它吐出的元函数类提供您想要的新参数:
unquote_t<std::vector<int>>::apply<std::string>
对于您的具体情况,只需quote
即可:
// I don't know what these things actually are, sorry
template <class Agent, class MF1, class MF2, class MF3>
struct World {
using t1 = MF1::template apply<Agent>;
using t2 = MF2::template apply<Agent>;
using t3 = MF3::template apply<Agent>;
};
World< NeuralNetworkAgent,
quote<EAPop>,
quote<MazeEnvironment>,
quote<LineageTracker>
> w;
您的实际问题可以通过获取模板模板参数来解决。
template <class Agent, template<class...> class F1,
template<class...> class F2,
template<class...> class F3>
struct World {
// use F1<Agent> etc.
};
World<NeuralNetworkAgent, EAPop, MazeEnvironment, LineageTracker > world;
@Barry的quote
是实现这一点的一种更为理想的方法,它对于更复杂的元编程非常有用,但对于如此简单的用例来说,IMO过于夸张了。
在C++中,不可能将任意模板专用化重新绑定到不同的模板参数集;最多可以处理一个子集(主要是只接受类型参数的模板,再加上您可能选择支持的其他一些组合),即使这样也会有很多问题。正确地重新绑定std::unordered_set<int, my_fancy_hash<int>, std::equal_to<>, std::pmr::polymorphic_allocator<int>>
需要特定于所用模板的知识。
- 如何反转整数参数包
- 使用C++库在Android项目中修改gradle中的cmake参数,用于插入指令的测试
- 如何使用默认参数等选择模板专业化
- 模板参数替换失败,并且未完成隐式转换
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 函数调用中参数的顺序重要吗
- 部分定义/别名模板模板参数
- HRESULT:将自定义代码与系统一个区分开
- 不能将类中的输入函数与带参数的函数分开
- 将QML数组和MAP/对象类型与C 区分开
- 用相同的参数将内部和外部构造函数分开
- 如何将执行路径与文件路径和参数分开
- 常量和常量引用参数之间的区别是什么
- int const函数(参数)、int函数(const参数)和int函数(parameters)const之间的区别是什
- 如果我只想要架构良好的工作区,但不一定是分开的编译,如何在C++中组织头/代码文件
- 二维数组参数之间的区别是什么?
- 与以值作为参数的普通函数相比,函子的区别是什么?
- 这两个可变参数函数之间的本质区别是什么?