我正在尝试找到一个迭代包变量模板参数列表的方法 . 现在和所有迭代一样,您需要某种方法来了解打包列表中有多少参数,更重要的是如何从打包参数列表中单独获取数据 .
一般的想法是迭代列表,将int类型的所有数据存储到向量中,将char *类型的所有数据存储到向量中,并将float类型的所有数据存储到向量中 . 在这个过程中,还需要一个单独的向量来存储参数进入顺序的各个字符 . 例如,当你使用push_back(a_float)时,你也在做一个push_back('f'),它只是存储一个单独的char来了解数据的顺序 . 我也可以在这里使用std :: string,只需使用= . 该矢量仅用作示例 .
现在,事物的设计方式是功能本身是使用宏构建的,尽管存在恶意,但它是必需的,因为这是一个实验 . 因此,使用递归调用几乎是不可能的,因为将容纳所有这些的实际实现将在编译时扩展;你不能招募一个宏 .
尽管有各种可能的尝试,我仍然坚持要弄清楚如何实际做到这一点 . 因此,我使用了一个更复杂的方法,包括构造一个类型,并将该类型传递到varadic模板,在向量中扩展它,然后简单地迭代它 . 但是我不想像以下那样调用函数:
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类型的争论,那将是非常高兴的 . 我很清楚代码是多么脆弱和破碎 . 这是merley的一个实验,我稍后可以解决非POD数据的问题,并使其更加类型安全和可用 .
感谢您的光临,我期待着您的帮助 .
7 回答
如果要将参数包装到
any
,可以使用以下设置 . 我还使any
类更有用,尽管从技术上讲它不是any
类 .但是,可以编写函数来访问可变参数模板函数中的第n个参数,并将函数应用于每个参数,这可能是执行任何操作的更好方法 .
这不是通常使用Variadic模板的方式,根本不是 .
根据语言规则,无法对可变参数包进行迭代,因此您需要转向递归 .
示例(在行动中),假设我们有
Stock stock;
:stock.push(1, 3.2f, 4, 5, 4.2f);
被解析为(a)因为第一个参数是int
this->push(args...)
扩展为this->push(3.2f, 4, 5, 4.2f);
,解析为(b),因为第一个参数是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);
,解析为(b),因为第一个参数是float
this->push(args...)
扩展为this->push();
,由于没有参数,因此解析为(c),从而结束递归从而:
添加另一个要处理的类型就像添加另一个重载一样简单,更改第一个类型(例如,
std::string const&
)如果传递完全不同的类型(例如
Foo
),则不能选择任何重载,从而导致编译时错误 .一个警告:自动转换意味着
double
将选择重载(b)和short
选择重载(a) . 如果不需要,那么需要引入SFINAE,这使得该方法稍微复杂一些(至少它们的签名),例如:is_int
的位置如下:但是,另一种选择是考虑变体类型 . 例如:
它已经存在,有了所有实用程序,它可以存储在_685807中,复制等等......并且看起来非常像你需要的,即使它不使用Variadic模板 .
您可以通过在{}之间使用参数包初始化它来创建容器 . 只要params的类型是同质的或者至少可以转换为容器的元素类型,它就可以工作 . (用g 4.6.1测试)
目前没有特定功能,但您可以使用一些解决方法 .
使用初始化列表
一种解决方法使用事实,initialization lists的子表达式按顺序进行评估 .
int a[] = {get1(), get2()}
将在执行get2
之前执行get1
. 也许fold expressions将来会用于类似的技术 . 要在每个参数上调用do()
,您可以执行以下操作:但是,这仅在
do()
返回int
时有效 . 您可以使用comma operator来支持不返回正确值的操作 .要做更复杂的事情,你可以把它们放在另一个函数中:
请注意,对于通用lambdas(C 14),您可以定义一个函数来为您执行此样板 .
使用递归
另一种可能性是使用递归 . 这是一个小例子,它定义了一个类似的函数
do_for
,如上所述 .你不能迭代,但你可以在列表上递归 . 检查维基百科上的printf()示例:http://en.wikipedia.org/wiki/C++0x#Variadic_templates
基于循环的范围很棒:
对我来说,this产生输出:
Here是一个没有
std::any
的例子,对于那些不熟悉std::type_info
的人来说可能更容易理解:正如您所料,这会产生:
你可以使用多个可变参数模板,这有点乱,但它的工作原理很容易理解 . 你只需要一个带有可变参数模板的函数,如下所示:
还有一个像这样的辅助函数:
现在,当您调用“function”时,将调用“helperFunction”并将第一个传递的参数与其余参数隔离,此变量可用于调用另一个函数(或其他内容) . 然后将一次又一次地调用“function”,直到不再有变量为止 . 注意,您可能必须在“function”之前声明helperClass .
最终代码如下所示:
代码未经过测试 .