如何解决检查 << 运算符是否适用于覆盖模板上下文
我创建了一个 AnyType
类,它类似于 C++14 中的 std::any。我想覆盖 <<
运算符以轻松打印存储变量的内容:
std::ostream& operator<<( std::ostream& o,const AnyType& v ) {
if( v._varPtr_ != nullptr ) v._varPtr_->writetoStream(o);
return o;
}
而 writetoStream()
是在 PlaceHolder
类中定义的重写函数,该类保存实际的类型变量。定义为:
void writetoStream(std::ostream& o) const override { o << _variable_; }
我的问题是,当我定义一个 AnyType
对象时,该对象包含一个没有覆盖 operator<<
的变量类型,编译器在展开定义的模板类时抛出(预期的)错误:
error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'const TGraph')
void writetoStream(std::ostream& o) const override { o << _variable_; }
~ ^ ~~~~~~~~~~
这是一个最小的例子:
struct PlaceHolder{
virtual ~PlaceHolder() = default;
virtual void writetoStream(std::ostream& o) const = 0;
};
template<typename VariableType> struct VariableHolder: public PlaceHolder{
explicit VariableHolder(VariableType value_) : _variable_(std::move(value_)){ }
void writetoStream(std::ostream& o) const override { o << _variable_; }
VariableType _variable_;
};
class AnyType{
public:
AnyType(){}
template<typename ValueType> inline void setValue(const ValueType& value_){
_varPtr_ = std::shared_ptr<VariableHolder<ValueType>>(new VariableHolder<ValueType>(value_));
}
friend std::ostream& operator<<( std::ostream& o,const AnyType& v ) {
if( v._varPtr_ != nullptr ) v._varPtr_->writetoStream(o);
return o;
}
private:
std::shared_ptr<PlaceHolder> _varPtr_{nullptr};
};
int main(){
AnyType a;
a.setValue(int(14));
std::cout << a << std::endl; // this will work
// If the following line is uncommented,the compiler will throw an error
// a.setValue(TGraph()); // this is a CERN's ROOT class
}
所以我的问题是:有什么方法可以让编译器检查是否可以在明确之前覆盖 <<
?这样我就可以让 writetoStream
方法被定义为默认值:= 0
。
可能还有另一种解决方法。通常,此问题不会出现在 std::any
中。有谁知道它是如何在标准库中实现的?
干杯:)
解决方法
是的,一般来说这是可能的,但前提是您的类 AnyType
是模板类。在这种情况下,您可以直接使用 std::enable_if
禁用 operator<<
。在您的情况下,AnyType
内的共享指针所持有的精确变量类型是在运行时决定的,您不能简单地使用 SFINAE 禁用它。您可以做的是在虚拟方法中使用类型特征来决定是否应该修改流,具体取决于VariableType
的模板参数VariableHolder
:
只需定义一个 type-trait is_streamable
as follows
template<typename S,typename T,typename = void>
struct is_streamable : std::false_type {
};
template<typename S,typename T>
struct is_streamable<S,T,decltype(std::declval<S&>() << std::declval<T&>(),void())> : std::true_type {
};
其中 S
对应于流(例如 std::ostream
),T
对应于要检查以流式传输到 S
中的数据类型。
然后使用它在 VariableHolder::writeToStream
中禁用它来决定是否应该修改流。使用 C++17,您可以使用 if constexpr
:
template<typename VariableType>
struct VariableHolder: public PlaceHolder {
explicit VariableHolder(VariableType value_)
: _variable_(std::move(value_)) {
return;
}
void writeToStream(std::ostream& o) const override {
// Only for streamable variable types
if constexpr (is_streamable<std::ostream,VariableType>::value) {
o << _variable_;
}
return;
}
VariableType _variable_;
};
在 C++11(根据要求)和 C++14 中,它有点复杂,您将不得不使用 helper 结构 和 部分专门它用于 true
(可流式传输类型)和 false
(非流式类型),然后使用我们之前引入的类型特征 is_streamable
调用它,如下所示: >
template <typename T,bool>
class helper {
};
// Partial specialisation for streamable variable types
template <typename T>
class helper<T,true> {
public:
static void imp(std::ostream& os,T const& t) {
// Stream variable t to stream os
os << t;
return;
}
};
// Partial specialisation for non-streamable variable types
template <typename T>
class helper<T,false> {
public:
static void imp(std::ostream&,T const&) {
// Leave stream os unmodified
return;
}
};
template<typename VariableType>
struct VariableHolder: public PlaceHolder {
explicit VariableHolder(VariableType value_)
: _variable_(std::move(value_)) {
return;
}
void writeToStream(std::ostream& o) const override {
// Call suiting helper function depending on template parameter
helper<VariableType,is_streamable<std::ostream,VariableType>::value>::imp(o,_variable_);
return;
}
VariableType _variable_;
};
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。