是否可以在 C++ 中将模板与其参数分开?
Is it possible to disentangle a template from its arguments in C++?
假设我收到模板的两个参数,T1 和 T2。如果我知道 T1 本身是一个模板化的 class(例如,一个容器),而 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< 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++ 问题。
这基本上是两个不同的问题:如何将 class 模板的实例分解为 class 模板,然后如何获取 class 模板并对其进行实例化。让我们遵循这样的原则:如果一切都是类型,那么模板元编程会更容易。
第一,第二部分。给定一个 class 模板,让我们把它变成一个元函数 class:
template <template <typename...> class F>
struct quote {
template <typename... Args>
using apply = F<Args...>;
};
这里,quote<std::vector>
是元函数class。它是具有成员模板 apply
的具体类型。所以 quote<std::vector>::apply<int>
给你 std::vector<int>
。
现在,我们需要解压一个类型。我们称它为 unquote
(至少这对我来说似乎合适)。这是一个接受类型并产生元函数 class:
的元函数
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
并将您想要的新参数提供给它吐出的元函数 class:
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>>
需要特定于所用模板的知识。
假设我收到模板的两个参数,T1 和 T2。如果我知道 T1 本身是一个模板化的 class(例如,一个容器),而 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< 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++ 问题。
这基本上是两个不同的问题:如何将 class 模板的实例分解为 class 模板,然后如何获取 class 模板并对其进行实例化。让我们遵循这样的原则:如果一切都是类型,那么模板元编程会更容易。
第一,第二部分。给定一个 class 模板,让我们把它变成一个元函数 class:
template <template <typename...> class F>
struct quote {
template <typename... Args>
using apply = F<Args...>;
};
这里,quote<std::vector>
是元函数class。它是具有成员模板 apply
的具体类型。所以 quote<std::vector>::apply<int>
给你 std::vector<int>
。
现在,我们需要解压一个类型。我们称它为 unquote
(至少这对我来说似乎合适)。这是一个接受类型并产生元函数 class:
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
并将您想要的新参数提供给它吐出的元函数 class:
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>>
需要特定于所用模板的知识。