我如何迭代打包variadic模板参数列表?
我试图找到一个方法来迭代一个pack variadic模板参数列表。 现在和所有迭代一样,您需要某种方法来了解打包列表中有多少个参数,更重要的是,如何从一个打包参数列表中单独获取数据。
总的想法是迭代列表,将int类型的所有数据存储到一个向量中,将所有类型为char *的数据存储到向量中,并将所有类型为float的数据存储到向量中。 在这个过程中,还需要一个单独的向量来存储参数进入顺序的个别字符。例如,当您push_back(a_float)时,您也正在执行一个push_back('f'),它只是存储一个单独的字符知道数据的顺序。 我也可以在这里使用std :: string,并简单地使用+ =。 该矢量仅用作示例。
现在这个东西的设计方式是使用宏来构造函数本身,尽管有恶意,但它是必需的,因为这是一个实验。 因此,使用递归调用实际上是不可能的,因为实际实现将容纳所有这些将在编译时被扩展; 你不能复制一个宏。
尽管做了所有可能的尝试,但我仍然坚持要弄清楚如何实际做到这一点。 所以相反,我使用更复杂的方法,包括构建一个类型,并将该类型传递给可变模板,将其扩展到一个向量中,然后对其进行迭代。 不过,我不想要像这样调用函数:
foo(arg(1), arg(2.0f), arg("three");
所以真正的问题是没有这样的我该怎么办? 为了让大家更好地理解代码实际在做什么,我粘贴了我目前使用的乐观方法。
struct any {
void do_i(int e) { INT = e; }
void do_f(float e) { FLOAT = e; }
void do_s(char* e) { STRING = e; }
int INT;
float FLOAT;
char *STRING;
};
template<typename T> struct get { T operator()(const any& t) { return T(); } };
template<> struct get<int> { int operator()(const any& t) { return t.INT; } };
template<> struct get<float> { float operator()(const any& t) { return t.FLOAT; } };
template<> struct get<char*> { char* operator()(const any& t) { return t.STRING; } };
#define def(name)
template<typename... T>
auto name (T... argv) -> any {
std::initializer_list<any> argin = { argv... };
std::vector<any> args = argin;
#define get(name,T) get<T>()(args[name])
#define end }
any arg(int a) { any arg; arg.INT = a; return arg; }
any arg(float f) { any arg; arg.FLOAT = f; return arg; }
any arg(char* s) { any arg; arg.STRING = s; return arg; }
我知道这很讨厌,但这是一个纯粹的实验,不会在生产代码中使用。 这完全是一个想法。 它可能可以做得更好。 但是你将如何使用这个系统的例子:
def(foo)
int data = get(0, int);
std::cout << data << std::endl;
end
看起来很像python。 它也可以工作,但唯一的问题是你如何调用这个函数。 下面是一个简单的例子:
foo(arg(1000));
我需要构建一个新的任何类型,这是非常美观的,但不是说这些宏都不是。 除了这一点,我只想选择做:foo(1000);
我知道它可以完成,我只需要某种迭代方法,或者更重要的是一些std :: get方法用于打包variadic模板参数列表。 我肯定可以完成。
另外值得注意的是,我很清楚这不是类型友好的,因为我只支持int,float,char *,并且这对我很好。 我不需要其他任何东西,并且我将添加检查以使用type_traits来验证传递的参数确实是正确的,如果数据不正确,则会产生编译时错误。 这纯粹不是问题。 除了这些POD类型,我也不需要支持任何东西。
如果我能够得到一些建设性的帮助,这将非常受欢迎,反对关于我纯粹不合逻辑和愚蠢地使用宏和POD类型的争论。 我很清楚代码是多么的脆弱和破碎。 这是一个很好的实验,我可以稍后纠正非POD数据的问题,并使其更具类型安全性和可用性。
感谢您的承诺,我期待着提供帮助。
如果您想将参数封装到any
,您可以使用以下设置。 我也让any
类更有用,尽管它在技术上不是any
类。
#include <vector>
#include <iostream>
struct any {
enum type {Int, Float, String};
any(int e) { m_data.INT = e; m_type = Int;}
any(float e) { m_data.FLOAT = e; m_type = Float;}
any(char* e) { m_data.STRING = e; m_type = String;}
type get_type() const { return m_type; }
int get_int() const { return m_data.INT; }
float get_float() const { return m_data.FLOAT; }
char* get_string() const { return m_data.STRING; }
private:
type m_type;
union {
int INT;
float FLOAT;
char *STRING;
} m_data;
};
template <class ...Args>
void foo_imp(const Args&... args)
{
std::vector<any> vec = {args...};
for (unsigned i = 0; i < vec.size(); ++i) {
switch (vec[i].get_type()) {
case any::Int: std::cout << vec[i].get_int() << 'n'; break;
case any::Float: std::cout << vec[i].get_float() << 'n'; break;
case any::String: std::cout << vec[i].get_string() << 'n'; break;
}
}
}
template <class ...Args>
void foo(Args... args)
{
foo_imp(any(args)...); //pass each arg to any constructor, and call foo_imp with resulting any objects
}
int main()
{
char s[] = "Hello";
foo(1, 3.4f, s);
}
然而,编写函数来访问变量模板函数中的第n个参数并将函数应用于每个参数是可能的,这可能是您做任何事情的更好方法。
这不是通常使用Variadic模板的方式,完全不是。
根据语言规则,不可能对变量包进行迭代,因此您需要转向递归。
class Stock
{
public:
bool isInt(size_t i) { return _indexes.at(i).first == Int; }
int getInt(size_t i) { assert(isInt(i)); return _ints.at(_indexes.at(i).second); }
// push (a)
template <typename... Args>
void push(int i, Args... args) {
_indexes.push_back(std::make_pair(Int, _ints.size()));
_ints.push_back(i);
this->push(args...);
}
// push (b)
template <typename... Args>
void push(float f, Args... args) {
_indexes.push_back(std::make_pair(Float, _floats.size()));
_floats.push_back(f);
this->push(args...);
}
private:
// push (c)
void push() {}
enum Type { Int, Float; };
typedef size_t Index;
std::vector<std::pair<Type,Index>> _indexes;
std::vector<int> _ints;
std::vector<float> _floats;
};
示例(在行动中),假设我们有Stock stock;
:
stock.push(1, 3.2f, 4, 5, 4.2f);
被解析为(a),因为第一个参数是一个int
this->push(args...)
被扩展到this->push(3.2f, 4, 5, 4.2f);
,这是解决(二)作为第一个参数是一个float
this->push(args...)
扩展为this->push(4, 5, 4.2f);
,它被解析为(a),因为第一个参数是一个int
this->push(args...)
扩展为this->push(5, 4.2f);
,它被解析为(a),因为第一个参数是一个int
this->push(args...)
扩展为this->push(4.2f);
,这是解决(二)作为第一个参数是一个float
this->push(args...)
扩展为this->push();
,因为没有参数,所以解决了(c),从而结束了递归 从而:
std::string const&
) Foo
),则不能选择超载,从而导致编译时错误。 一个警告:自动转换意味着double
选择超载(b), short
选择超载(a)。 如果不需要,那么就需要引入SFINAE,这使得方法稍微复杂一些(以及它们的签名至少),例如:
template <typename T, typename... Args>
typename std::enable_if<is_int<T>::value>::type push(T i, Args... args);
其中is_int
会是这样的:
template <typename T> struct is_int { static bool constexpr value = false; };
template <> struct is_int<int> { static bool constexpr value = true; };
不过,另一种选择是考虑变体类型。 例如:
typedef boost::variant<int, float, std::string> Variant;
它已经存在,所有的实用程序,它可以存储在一个vector
,复制等等,并且看起来非常像你所需要的,即使它不使用可变模板。
您可以通过在{}之间使用参数包对其进行初始化来创建容器。 只要参数的类型是同类的或至少可以转换为您的容器的元素类型,它就可以工作。 (用g ++ 4.6.1测试)
#include <array>
template <class... Params>
void f(Params... params) {
std::array<int, sizeof...(params)> list = {params...};
}
链接地址: http://www.djcxy.com/p/51573.html
上一篇: How can I iterate over a packed variadic template argument list?