我遇到了一个我真不知道如何解决的问题。我想在构造函数中使用{ }
括号传递可变参数对,但由于某种原因,我得到了compintion错误。我让课堂尽可能简单
class MyPairs
{
public:
MyPairs(std::pair<char, int>&& pair)
{
pairs[pair.first] = pair.second;
}
template <typename ... Args>
MyPairs(std::pair<char, int>&& pair, Args&& ... args)
{
pairs[pair.first] = pair.second;
MyPairs(std::forward<Args>(args)...);
}
private:
std::map<char, int> pairs;
};
int main()
{
MyPairs p1( // okay, constructor expects pairs
std::pair<char, int>('a', 1),
std::pair<char, int>('b', 2)
);
MyPairs p2( // okay, compiler knows that it is std::pair<char, int> in constructor
{ 'c',3 }
);
MyPairs p3( // error, for some reason now he doesnt know it is std::pair<char, int>, std::pair<char, int>
{ 'd',6 },
{ 'e',5 }
);
}
当然,我可以为每个参数写std::pair<char, int>('a', 1)
,但{'a', 1}
更加方便。
为什么编译器在使用{ }
括号时不知道如何匹配函数?
答案 0 :(得分:2)
专注于这个问题:
为什么编译器在使用
{ }
括号时不知道如何匹配函数?
原因是因为每个参数都是单独推导出来的。例如,考虑一下:
template <typename A, typename B>
void foo(A a, B b) {
}
如果我致电foo(1, 3.14)
,则A
将被推断为int
,而B
将被推断为double
。这两个参数完全分开处理。第一个int
的事实并没有告诉我们关于第二个参数的任何信息。编译器必须独立于第一个参数推导出第二个参数。
同样的逻辑适用于可变参数模板。可变参数模板中的每个参数都是单独处理的。构造函数的第一个参数是std::pair<char, int>
的事实告诉您 nothing 关于后面的参数。毕竟,你可以试试这个:
MyPairs p(
{ 'd',6 },
1,
3.14,
"Hello, world!"
);
并且编译器将调用可变参数构造函数并将可变参数推导为int
,double
和字符串。每个参数都是单独处理的。
因此,当您尝试以下操作时:
MyPairs p(
{ 'd',6 },
{ 'e',5 }
);
编译器必须尝试推导出{ 'e',5 }
的类型。它应该是什么类型的?请记住,编译器必须与第一个参数分开推导出它的类型。我们人类很清楚你希望它是std::pair
,但编译器并不知道这一点。所以编译器会给出一个错误,说它不知道如何处理这些额外的参数。
举一个稍微简单的例子,你的代码大致相当于这样做:
template <typename A>
void bar(A a) {
}
bar({ 'd', 6 });
编译器不清楚您希望将A
推断为std::pair
。毕竟,它可能是很多不同的东西。
答案 1 :(得分:1)
最简单的方法可能是使用list initialization,并提供一个接受std::initializer_list<T>
的构造函数。
事实上,std::map's constructor(5)就是这样做的。
例如:
MyPairs(std::initializer_list<std::map<char, int>::value_type> init) : pairs(init) {}
这里唯一棘手的问题是我们无法使用std::intializer_list<std::pair<char, int>>
(因为std::map<Key, T>
将value_type
定义为std::pair<const Key, T>
),因为这不会转换为{{ 1}} map的构造函数是期待的。