initializer_list和模板类型推导

问题描述 投票:0回答:4

考虑功能:

template<typename T>
void printme(T&& t) {
  for (auto i : t)
    std::cout << i;
}

或任何其他需要一个参数具有 begin()/end() 启用类型的函数。

为什么以下行为是非法的?

printme({'a', 'b', 'c'});

当所有这些都是合法的时:

printme(std::vector<char>({'a', 'b', 'c'}));
printme(std::string("abc"));
printme(std::array<char, 3> {'a', 'b', 'c'});

我们甚至可以这样写:

const auto il = {'a', 'b', 'c'};
printme(il);

printme<std::initializer_list<char>>({'a', 'b', 'c'});
c++ templates c++11 initializer-list
4个回答
53
投票

您的第一行

printme({'a', 'b', 'c'})
是非法的,因为无法推断模板参数
T
。如果您明确指定模板参数它将起作用,例如
printme<vector<char>>({'a', 'b', 'c'})
printme<initializer_list<char>>({'a', 'b', 'c'})

您列出的其他参数是合法的,因为参数具有明确定义的类型,因此可以很好地推导模板参数

T

带有

auto
的代码片段也有效,因为
il
被认为是
std::initializer_list<char>
类型,因此可以推导出
printme()
的模板参数。


这里唯一“有趣”的部分是

auto
将选择类型
std::initializer_list<char>
但模板参数不会。这是因为 C++11 标准的第 14.8.2.5/5 节明确指出这是模板参数的非推导上下文:

关联参数是初始化列表(8.5.4)但参数没有 std::initializer_list 或对可能 cv 限定的 std::initializer_list 类型的引用的函数参数。 [示例:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

—结束示例]

但是,对于

auto
,§ 7.1.6.4/6 明确支持
std::initializer_list<>

如果初始化器是 braced-init-list (8.5.4),带有

std::initializer_list<U>


14
投票

您还可以重载该函数以显式采用initializer_list类型的参数。

template<typename T>
void printme(std::initializer_list<T> t) {
  for (auto i : t)
    std::cout << i;
}

6
投票

这在第 § 14.8.2.5/5 条中有明确规定

关联参数是一个函数参数 初始化列表但参数没有

std::initializer_list
或参考可能符合简历要求的内容
std::initializer_list
类型。 [ 示例:

template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T

—示例结束]

要使其正常工作,您可以显式指定模板参数类型。

printme<std::initializer_list<int>>( {1,2,3,4} );

0
投票

我找到了一个解决方案,该解决方案不适用于问题中提供的

printme
功能,但适用于类似的功能:

这就是我开始的地方:

template <typename V, typename C>
bool in(const C& container, const V& element)
{
    return std::find(container.begin(), container.end(), element) != container.end();
}

无法编译,例如:

in({1, 2, 3}, 1);

Candidate template ignored: couldn't infer template argument 'C'

基于此处的另一个答案,我们可以为

std::initializer_list
添加模板专业化:

template <typename V, typename C>
bool in(const C& container, const V& element)
{
    return std::find(container.begin(), container.end(), element) != container.end();
}
template <typename V>
bool in(const std::initializer_list<V>& container, const V& element)
{
    return std::find(container.begin(), container.end(), element) != container.end();
}

这看起来很傻。

但以下方法也有效:

template <typename V, typename C = std::initializer_list<V>>
bool in(const C& container, const V& element)
{
    return std::find(container.begin(), container.end(), element) != container.end();
}
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.