问题
I encountered a problem which i dont really know how to solve. I want to pass variadic ammount of pairs in constructor, using { }
braces, but for some reason im gettin compialtion error. I kept class as simple as possible
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 }
);
}
Of course i can write std::pair<char, int>('a', 1)
for every argument, but {'a', 1}
is much more conventient.
Why compiler doesnt know how to match function when using { }
braces ?
回答1:
To focus on this question:
Why compiler doesn't know how to match function when using
{ }
braces?
The reason is because each parameter is deduced separately. For example, consider this:
template <typename A, typename B>
void foo(A a, B b) {
}
If I call foo(1, 3.14)
then A
will be deduced as int
and B
will be deduced as double
. The two parameters are treated totally separately. The fact that the first one is int
does not tell us anything about the second parameter. The compiler has to deduce the second parameter all on its own, independent of the first parameter.
This same logic applies to variadic templates. Each parameter in a variadic template is treated individually. The fact that the first parameter to your constructor is a std::pair<char, int>
tells you nothing about the later parameters. You could, after all, try this:
MyPairs p(
{ 'd',6 },
1,
3.14,
"Hello, world!"
);
and the compiler would call the variadic constructor and deduce the variadic arguments as int
, double
, and a string. Each parameter is treated individually.
So when you try the following:
MyPairs p(
{ 'd',6 },
{ 'e',5 }
);
The compiler has to try and deduce the type of { 'e',5 }
. And what type should it have? Remember, the compiler has to deduce its type separately from the first parameter. It's clear to us humans that you want it to be std::pair
, but there compiler does not know that. So the compiler gives an error saying it doesn't know what to do with those extra parameters.
To give a slightly simpler example, your code is roughly equivalent to doing this:
template <typename A>
void bar(A a) {
}
bar({ 'd', 6 });
It's not clear to the compiler that you want A
to be deduced as a std::pair
. It could, after all, be a whole lot of different things.
回答2:
The easiest way is probably to use list initialization, and provide a constructor that accepts an std::initializer_list<T>
.
In fact, that's how std::map's constructor (5) does it.
For example:
MyPairs(std::initializer_list<std::map<char, int>::value_type> init) : pairs(init) {}
See it live on Coliru
The only tricky bit here is that we cannot use a std::intializer_list<std::pair<char, int>>
(because std::map<Key, T>
defines value_type
as std::pair<const Key, T>
), since that will not be convertible to the std::intializer_list<std::pair<const char, int>>
map's constructor is expecting.
来源:https://stackoverflow.com/questions/34482116/passing-multiple-stdpair-to-variadic-template-constructor-using