如何约束参数仅支持std::initializer_list<size_t>或std::array<size_t,N>且用单模板函数?
解决模板
operator<<匹配std::array和initializer_list的问题 你的问题主要出在两个地方:一是模板参数里的size_t N会让std::initializer_list无法匹配(因为它没有这个模板参数,编译器推导不出N的值);二是函数参数用了非const引用Iteratable&,而initializer_list是临时对象,没法绑定到非const引用上。
不用写两个函数完全可以解决,这里给你两种方案:
方案1:匹配所有可迭代的size_t容器(简单通用)
如果你不介意这个operator<<也能处理std::vector<size_t>这类其他容器,那可以直接检查类型是否可迭代且元素是size_t,代码更简洁:
#include <iostream> #include <iterator> #include <algorithm> #include <type_traits> #include <array> #include <initializer_list> template<typename Iterable> std::enable_if_t< std::is_convertible_v<decltype(*std::begin(std::declval<const Iterable>())), size_t>, std::ostream& > operator << (std::ostream& os, const Iterable& in) { std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ")); return os; } // 测试代码 int main() { std::array<size_t, 3> arr{1,2,3}; std::cout << arr << "\n"; std::cout << {4,5,6} << "\n"; return 0; }
这个方案通过std::is_convertible_v检查容器的元素是否能转为size_t,同时利用std::begin/std::end的ADL特性支持所有标准容器和initializer_list。
方案2:严格仅匹配目标类型(精准控制)
如果你只想让这个operator<<作用于const std::initializer_list<size_t>和任意大小的const std::array<size_t, N>,可以用SFINAE结合类型特性来判断:
C++20版本(利用标准库的std::is_specialization_of)
#include <iostream> #include <iterator> #include <algorithm> #include <type_traits> #include <array> #include <initializer_list> template<typename Iterable> std::enable_if_t< std::disjunction_v< std::is_same<Iterable, const std::initializer_list<size_t>>, std::conjunction_v< std::is_same<typename Iterable::value_type, size_t>, std::is_specialization_of<Iterable, std::array> > >, std::ostream& > operator << (std::ostream& os, const Iterable& in) { std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ")); return os; }
C++17兼容版本(自定义is_specialization_of)
因为C++17没有标准的is_specialization_of,我们可以自己实现一个类型特性:
#include <iostream> #include <iterator> #include <algorithm> #include <type_traits> #include <array> #include <initializer_list> // 自定义类型特性:判断T是否是Template的特化 template<typename T, template<typename...> typename Template> struct is_specialization_of : std::false_type {}; template<template<typename...> typename Template, typename... Args> struct is_specialization_of<Template<Args...>, Template> : std::true_type {}; template<typename T, template<typename...> typename Template> constexpr bool is_specialization_of_v = is_specialization_of<T, Template>::value; // 核心的operator<<模板 template<typename Iterable> std::enable_if_t< std::disjunction_v< std::is_same<Iterable, const std::initializer_list<size_t>>, std::conjunction_v< std::is_same<typename Iterable::value_type, size_t>, is_specialization_of_v<Iterable, std::array> > >, std::ostream& > operator << (std::ostream& os, const Iterable& in) { std::copy(std::begin(in), std::end(in), std::ostream_iterator<size_t>(os, " ")); return os; }
方案说明
- 我们把
size_t N从模板参数列表中移除了,转而通过is_specialization_of判断Iterable是否是std::array的特化,这样就不用依赖N的推导了。 std::disjunction_v用来做逻辑或,只要满足initializer_list或者std::array<size_t, N>其中一个条件就可以匹配。- 函数参数改成
const Iterable&,这样可以正确绑定临时的initializer_list对象。
内容的提问来源于stack exchange,提问作者sandthorn
相关产品推荐
相关产品推荐

