使用{},{}将多个std :: pair传递给variadic模板构造函数

时间:2015-12-27 15:57:09

标签: c++ templates c++11 variadic-templates

我遇到了一个我真不知道如何解决的问题。我想在构造函数中使用{ }括号传递可变参数对,但由于某种原因,我得到了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}更加方便。

为什么编译器在使用{ }括号时不知道如何匹配函数?

2 个答案:

答案 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!"
);

并且编译器将调用可变参数构造函数并将可变参数推导为intdouble和字符串。每个参数都是单独处理的。

因此,当您尝试以下操作时:

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) {}

See it live on Coliru

这里唯一棘手的问题是我们无法使用std::intializer_list<std::pair<char, int>>(因为std::map<Key, T>value_type定义为std::pair<const Key, T>),因为这不会转换为{{ 1}} map的构造函数是期待的。