C++自定义类似std::any的AnyType类重载<<运算符编译错误求解
编译期检查类型可打印性的实现
你可以通过自定义类型特征配合static_assert在setValue调用阶段就完成检查,避免模板展开到内部才抛出模糊的编译错误:
- 首先定义可打印性判断的类型特征:
#include <type_traits> #include <ostream> #include <typeinfo> // C++17及以上可用std::void_t,C++11/14可自行实现void_t template<typename... Ts> struct make_void { typedef void type; }; template<typename... Ts> using void_t = typename make_void<Ts...>::type; template<typename T, typename = void> struct is_ostreamable : std::false_type {}; template<typename T> struct is_ostreamable<T, void_t<decltype(std::declval<std::ostream&>() << std::declval<const T&>())>> : std::true_type {}; template<typename T> constexpr bool is_ostreamable_v = is_ostreamable<T>::value;
- 在
setValue方法中添加静态断言:
template<typename ValueType> inline void setValue(const ValueType& value_){ static_assert(is_ostreamable_v<ValueType>, "The type stored in AnyType must support operator<< with std::ostream"); _varPtr_ = std::shared_ptr<VariableHolder<ValueType>>(new VariableHolder<ValueType>(value_)); }
此时如果传入不支持<<的类型,编译器会直接在setValue调用位置抛出你自定义的错误提示,排查成本极低。
兼容不可打印类型的替代方案
如果你希望不支持打印的类型也能正常存入AnyType,只是打印时输出兜底提示,可以用if constexpr(C++17及以上)修改writeToStream的实现,不需要编译期强制拦截:
template<typename VariableType> struct VariableHolder: public PlaceHolder{ explicit VariableHolder(VariableType value_) : _variable_(std::move(value_)){ } void writeToStream(std::ostream& o) const override { if constexpr (is_ostreamable_v<VariableType>) { o << _variable_; } else { // 输出不可打印的类型提示,可选输出typeid名称方便排查 o << "<unprintable type: " << typeid(VariableType).name() << ">"; } } VariableType _variable_; };
C++11/14环境下可以用标签派发实现相同的分支逻辑。
std::any没有该问题的原因
标准库的std::any只负责实现类型擦除的存储能力,本身没有提供operator<<重载,也没有内置任何打印相关的逻辑。所有对存储值的操作都需要用户主动调用std::any_cast转换为原类型后自行处理,自然不会在存入阶段就检查类型是否支持<<运算符。
内容的提问来源于stack exchange,提问作者nadrino
相关产品推荐
相关产品推荐

