我正在为机器学习编写 API,我需要一个重载函数,该函数可以接收向量作为参数,也可以接收向量向量(用于批处理作业)。
不过,我在调用该函数时遇到了一些问题。
作为一个更简单的示例,该函数可能如下所示:
void bar( const std::vector<float>& arg ) {
std::cout << "BAR: Vector of float" << std::endl;
}
void bar( const std::vector<std::vector<float>>& arg ) {
std::cout << "BAR: Vector of vectors of float" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
所以,我希望我可以这样称呼它:
bar( { 1,2,3 } );
bar( { { 1,2,3 } } );
Run Code Online (Sandbox Code Playgroud)
但是在第二个 IDE 中,IDE 抱怨说,两个重载函数都匹配参数列表,因此我必须像这样调用它才能使其工作。
bar( { { { 1,2,3 } } } );
Run Code Online (Sandbox Code Playgroud)
这是为什么?那不是向量的向量(即“3D-向量”)吗?
当我传递一个先前初始化的向量时也是如此:
std::vector<float> v = { 1,2,3,4,5 };
bar( v );
bar( { v } );
Run Code Online (Sandbox Code Playgroud)
两者都打印出BAR: Vector of float消息。所以我现在必须去:
bar( { { { v } } } );
Run Code Online (Sandbox Code Playgroud)
让它工作,这现在看起来像一个 4D 向量。我错过了什么吗?
欢迎来到地狱。当你有
bar( { 1,2,3 } );
Run Code Online (Sandbox Code Playgroud)
将{ 1,2,3 }被视为一个std::initializer_list<float>和唯一可行的函数调用void bar( const std::vector<float>& arg )
当你有
bar( { { 1,2,3 } } );
Run Code Online (Sandbox Code Playgroud)
现在{ { 1,2,3 } }可以被解释为表示 a 的外部大括号std::vector<float>,内部大括号表示std::initializer_list<float>它,或者可以std::initializer_list<std::initializer_list<float>>是用于构造 2d 向量的 a 的构造。任何一个选项都一样好,所以你会产生歧义。正如你所发现的,解决方案是
bar( { { { 1,2,3 } } } );
Run Code Online (Sandbox Code Playgroud)
所以现在最外面的一组大括号表示创建 a std::vector<std::vector<float>>,第二个最外面的大括号表示 a 的开始,最std::initializer_list<std::initializer_list<float>>里面的大括号是 a 的一个元素。
和
bar( v );
bar( { v } );
Run Code Online (Sandbox Code Playgroud)
它有点复杂。显然bar( v );会做你想做的事,但bar( { v } );实际上与[over.ics.list]bar( { { 1,2,3 } } );中的规则不同。具体来说,第 7 段说这是通过复制构造函数创建 a的精确匹配,而创建 a是用户定义的转换。这意味着跟注是更好的匹配,这就是您所看到的。你需要使用{ v }std::vector<float>std::vector<std::vector<float>>void bar( const std::vector<float>& arg )
bar( { { { v } } } );
Run Code Online (Sandbox Code Playgroud)
这样外面的一组大括号表示std::vector<std::vector<float>>,中间的一组是 的开始,std::initializer_list<std::vector<float>>最里面的一组是该std::vector<float>列表的单个元素。