为什么我的初始值设定项列表会因类型扣除而被忽略?

时间:2016-12-09 16:12:07

标签: c++ c++11 variadic-templates initializer-list type-deduction

请考虑以下代码:

#include <unordered_map>
#include <iostream>
#include <vector>

template <typename Container, typename... Containers>
inline Container get_union(
    const Container& c1,
    const Containers&... more_containers)
{
    Container result(c1);
    auto f = [&result](const Container& c) {
        result.insert(std::begin(c), std::end(c)); 
    };
    [](...){}(( f(more_containers), 0)...);
    return result;
}

int main()
{
    std::unordered_map<int, int> m1 = { {1, 2}, {3, 4} };
    decltype(m1) m2 = get_union(m1, { {5, 6}, {7, 8} } );
    std::cout << "m2.size() = " << m2.size() << "\n'";
    return 0;
}

当我尝试构建时,我get(coliru链接):

g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp: In function 'int main()':
main.cpp:21:56: error: too many arguments to function 'Container get_union(const Container&, const Containers& ...) [with Container = std::unordered_map<int, int>; Containers = {}]'
     decltype(m1) m2 = get_union(m1, { {5, 6}, {7, 8} } );
                                                        ^
main.cpp:6:18: note: declared here
 inline Container get_union(
                  ^~~~~~~~~

为什么编译器选择容器类型的空类型包,如果这会导致错误?

2 个答案:

答案 0 :(得分:4)

来自list initialization的cppreference:

  

braced-init-list不是表达式,因此没有类型,例如decltype({1,2})格式不正确。没有类型意味着模板类型推导不能推断出与braced-init-list匹配的类型,因此在声明template<class T> void f(T);的情况下,表达式f({1,2,3})是不正确的。

  

为什么编译器为容器类型选择空类型包

由于模板参数包的含义模糊不清,有两种可能的解释方法(我认为)。由于两者都不是格式良好,我不确定标准是否规定哪种解释是正确的。

  • 参数包的大小为1,在这种情况下,单个类型将从格式错误的braced-init-list中推断出来。

  • 或者,由于类型的任何内容都没有超出第一个参数,因此空参数包是Containers唯一允许的推导类型。现在,由于推导函数只接受一个参数,因此braced-init-list太多了。这就是编译器选择解释的方式。

从根本上说,问题在于您尝试循环扣除。您显然试图从Containers中推断出类型的内容推断Containers - 如果它不是空包。

答案 1 :(得分:1)

调用函数时,无法从{}推断出来。

template <typename Container, typename... Containers>
inline Container get_union(
  Container c1,
  std::initializer_list<Container> more_containers)
{
  for (auto&& c:more_containers)
    c1.insert( std::begin(c), std::end(c) );
  return c1;
}
然后打电话给:

auto m2 = get_union(m1, { { {5, 6}, {7, 8} } } );

live example