修改C++17版output_container.h适配C++11遇模板实例化深度超限问题
问题根因
- 核心是模板递归实例化死循环:你定义的通用
operator<<的启用条件是!has_output_function<T>::value,而has_output_function检测类型是否有operator<<时,会把你自己写的这个通用operator<<纳入重载决议,形成「要计算has_output_function的值需要先判断operator<<是否可用,判断operator<<是否可用需要先计算has_output_function的值」的无限递归,最终超过模板实例化深度上限。 - 次要问题:C++11不支持
if constexpr语法,你代码中保留的if constexpr会被当做普通运行时if处理,两个分支都会被强制实例化,进一步加重模板匹配的异常。 - 额外问题:C++11不支持
inline constexpr变量语法,全局inline constexpr会导致编译错误。
修复步骤
- 新增容器类型检测逻辑,明确只有符合「有begin/end成员、且不是原生字符串/
std::string类型」的类型才会启用我们的通用operator<<,避免错误匹配到不需要处理的类型。 - 将通用
operator<<移入独立命名空间,避免has_output_function检测时匹配到我们自定义的重载,切断递归依赖。 - 把
if constexpr替换为C++11兼容的标签分发实现编译期分支选择。 - 删除所有变量前的
inline关键字,兼容C++11的constexpr语法规则。
修改后完整兼容C++11的代码
#ifndef OUTPUT_CONTAINER_H #define OUTPUT_CONTAINER_H #include <ostream> // std::ostream #include <type_traits> // std::false_type/true_type/decay/is_same #include <utility> // std::declval/pair #include <string> // 用于排除std::string类型 // 检测是否为std::pair的类型特性 template <typename T> struct is_pair : std::false_type {}; template <typename T, typename U> struct is_pair<std::pair<T, U>> : std::true_type {}; template <typename T> constexpr bool is_pair_v = is_pair<T>::value; // 自定义C++11兼容的类型别名 template< class T > using decay_tt = typename std::decay<T>::type; template< class T, class U > constexpr bool is_same_vv = std::is_same<T, U>::value; // 检测是否为可输出的容器(排除字符串类型) template <typename T> struct is_container { template <typename U> static auto test(U* ptr) -> decltype(ptr->begin(), ptr->end(), std::true_type()); template <typename U> static std::false_type test(...); static constexpr bool value = decltype(test<T>(nullptr))::value && !is_same_vv<decay_tt<T>, const char*> && !is_same_vv<decay_tt<T>, char*> && !is_same_vv<decay_tt<T>, std::string>; }; // 检测是否已存在输出函数的类型特性 template <typename T> struct has_output_function { template <class U> static auto output(U* ptr) -> decltype(std::declval<std::ostream&>() << *ptr, std::true_type()); template <class U> static std::false_type output(...); static constexpr bool value = decltype(output<T>(nullptr))::value; }; template <typename T> constexpr bool has_output_function_v = has_output_function<T>::value; /* 注意:Visual Studio 2017及更早版本可能对has_output_function_v<T>支持有问题,可改用has_output_function<T>::value,或升级到Visual Studio 2019 */ // std::pair的输出函数 template <typename T, typename U> std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& pr); // 针对定义了key_type且值类型为std::pair的容器的元素输出函数 template <typename T, typename Cont> auto output_element(std::ostream& os, const T& element, const Cont&, std::true_type) -> decltype(std::declval<typename Cont::key_type>(), os); // 其他容器的元素输出函数 template <typename T, typename Cont> auto output_element(std::ostream& os, const T& element, const Cont&, ...) -> decltype(os); // 独立命名空间存放通用容器operator<<,避免has_output_function检测时递归匹配 namespace output_container_impl { // 标签分发实现不同元素类型的容器输出 template <typename T> void output_cont(std::ostream& os, const T& container, std::true_type /* 元素是char类型 */) { for (auto it = container.begin(); it != container.end(); ++it) { if (*it == '\0') { break; } os << *it; } } template <typename T> void output_cont(std::ostream& os, const T& container, std::false_type /* 元素不是char类型 */) { os << '{'; auto end = container.end(); bool on_first_element = true; for (auto it = container.begin(); it != end; ++it) { if (!on_first_element) { os << ", "; } else { os << ' '; on_first_element = false; } using element_type = decay_tt<decltype(*it)>; output_element(os, *it, container, is_pair<element_type>()); } if (!on_first_element) { os << ' '; } os << '}'; } template <typename T, typename = typename std::enable_if< !has_output_function<T>::value && is_container<T>::value >::type> auto operator<<(std::ostream& os, const T& container) -> decltype(container.begin(), container.end(), os) { using element_type = decay_tt<decltype(*container.begin())>; constexpr bool is_char_v = is_same_vv<element_type, char>; output_cont(os, container, std::integral_constant<bool, is_char_v>()); return os; } } // namespace output_container_impl // 导出命名空间内的operator<<到全局,确保ADL查找可以匹配 using namespace output_container_impl; template <typename T, typename Cont> auto output_element(std::ostream& os, const T& element, const Cont&, std::true_type) -> decltype(std::declval<typename Cont::key_type>(), os) { os << element.first << " => " << element.second; return os; } template <typename T, typename Cont> auto output_element(std::ostream& os, const T& element, const Cont&, ...) -> decltype(os) { os << element; return os; } template <typename T, typename U> std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& pr) { os << '(' << pr.first << ", " << pr.second << ')'; return os; } #endif // OUTPUT_CONTAINER_H
使用说明
直接替换你原来的output_container.h即可,测试代码无需修改,使用-std=c++11及以上标准编译都可以正常运行。
内容的提问来源于stack exchange,提问作者hao zheng
相关产品推荐
相关产品推荐

