如何将模板类中两个()运算符重载签名合并为一个?
问题描述
我在头文件header.h中定义了如下结构体:
#pragma once #include <type_traits> #include <iostream> #include <sstream> struct foo { // 工具结构体 template <class T, class... Ts> struct is_any: std::disjunction <std::is_same <T, Ts>... >{}; // 标准场景:传入指定流对象 template <class T_os, class T, class... Args, typename = std::enable_if_t<is_any <T_os, std::ostream, std::ostringstream>::value>> const foo& operator () ( T_os& os, const T& first, const Args&... args ) const { os << "hello"; return *this; } // 默认场景:未传入流时用std::cout template <class T, class... Args> const foo& operator () ( const T& first, const Args&... args ) const { std::cout << "hello"; return *this; } };
我为该结构体重载了两个()运算符:
- 标准场景:当
T_os类型为std::ostream或std::ostringstream时启用模板,使用传入的os对象输出消息; - 默认场景:未显式传入
T_os时,使用std::cout输出消息。
示例调用代码如下:
#include "header.h" int main() { foo foo_obj; // 标准场景调用 foo_obj ( std::cout, "first", "second" ); // 默认场景调用 foo_obj ( "first", "second" ); }
请问能否将这两个()运算符重载合并为一个,以支持上述两种调用操作?
解决方案
完全可以合并成一个重载,核心思路是通过类型判断区分首个参数是否为合法流对象,再分支处理输出逻辑。这里提供两种简洁的实现方式:
方式一:constexpr分支判断(最直观)
利用编译期constexpr判断首个参数的类型,直接在重载函数内部分支处理两种场景:
#pragma once #include <type_traits> #include <iostream> #include <sstream> struct foo { template <class T, class... Ts> struct is_any : std::disjunction<std::is_same<T, Ts>...> {}; // 合并后的单重载版本 template <class TFirst, class... Args> const foo& operator()(TFirst&& first, Args&&... args) const { // 编译期判断首个参数是否为合法流类型 if constexpr (is_any<std::decay_t<TFirst>, std::ostream, std::ostringstream>::value) { // 场景1:首个参数是流,直接用它输出 first << "hello"; // 处理后续参数(原代码未使用,可按需扩展) ((void)args, ...); } else { // 场景2:首个参数不是流,用std::cout输出 std::cout << "hello"; // 处理所有传入的参数 (void)first; ((void)args, ...); } return *this; } };
方式二:内部实现函数+标签分发
如果需要更清晰的职责分离,可以把输出逻辑抽成内部实现函数,通过标签分发调用不同分支:
#pragma once #include <type_traits> #include <iostream> #include <sstream> struct foo { template <class T, class... Ts> struct is_any : std::disjunction<std::is_same<T, Ts>...> {}; private: // 内部实现:带指定流的输出逻辑 template <class TOs, class... Args> const foo& impl_with_stream(TOs& os, Args&&... args) const { os << "hello"; ((void)args, ...); return *this; } public: // 合并后的重载入口 template <class TFirst, class... Args> const foo& operator()(TFirst&& first, Args&&... args) const { if constexpr (is_any<std::decay_t<TFirst>, std::ostream, std::ostringstream>::value) { return impl_with_stream(std::forward<TFirst>(first), std::forward<Args>(args)...); } else { return impl_with_stream(std::cout, std::forward<TFirst>(first), std::forward<Args>(args)...); } } };
效果验证
两种实现都完全兼容原有的两种调用方式:
- 调用
foo_obj(std::cout, "first", "second")时,会识别出首个参数是合法流,直接使用std::cout输出; - 调用
foo_obj("first", "second")时,会自动使用默认的std::cout输出,同时处理传入的参数。
内容的提问来源于stack exchange,提问作者Gianluca Bianco
相关产品推荐
相关产品推荐

