我想以这种样式在std::ostream
上写一个包装器:
#include <iostream>
struct OstreamWrapper {
OstreamWrapper(std::ostream &out) : out(out) {}
template< typename T >
decltype(auto) operator<<(T &&arg) {
return out << std::move< T >(arg);
}
std::ostream &out;
};
int main() {
OstreamWrapper wrap(std::cout);
wrap << "Hello, world!"; // This works
wrap << std::endl; // This does not work
return 0;
}
此方法的问题是,它std::endl
不起作用(例如),因为(据我所知)std::endl
已超载,并且编译器不知道如何解决该问题。评估模板时超载。
我相信可以通过一些聪明的SFINAE来解决这种情况,但是我找不到可行的方法。我想我需要类似“仅在cout << arg
是格式正确的表达式时才启用此模板”之类的东西,但是我不知道如何表达它。
例如,我尝试过:
template< typename T,
typename = decltype(out << arg) >
decltype(auto) operator<<(T &&arg) {
return out << std::move< T >(arg);
}
但这不是可以的,因为然后评估了模板表达式,尚未定义arg。
template< typename T,
typename = decltype(out << std::declval< T >()) >
decltype(auto) operator<<(T &&arg) {
return out << std::move< T >(arg);
}
这可以编译,但是不执行我想要的操作,因为它要求知道类型T
,而我的问题实际上在于确定如何重载其参数。
我还尝试了基于std::enable_if
和std::is_invocable
和std::result_of
的晦涩条件,但是它们引入了许多我无法理解的错误,这里总结可能毫无意义。所有尝试。
有没有办法正确地执行此操作?可能与C ++ 14一起使用,因此代码库仍具有更多的向后兼容性,但是如果需要C ++ 17,也可以。
答案 0 :(得分:5)
您可以将重载添加为强制类型(因此选择了唯一的可用重载):
struct OstreamWrapper {
explicit OstreamWrapper(std::ostream &out) : out(out) {}
template< typename T >
decltype(auto) operator<<(T &&arg) {
return out << std::forward<T>(arg);
}
decltype(auto) operator<<(std::ostream& (&arg)(std::ostream&)) {
return out << arg;
}
std::ostream &out;
};
答案 1 :(得分:1)
std::endl
未超载。是这样声明的功能模板:
template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );
它直接适用于std::ostream
的原因是适当的operator <<
(用于流操纵器的那个)是常规成员函数(尽管是从{{1 }})。它需要一种具体的机械手类型。模板参数推导可与此参数类型一起使用,以推导正确的basic_ostream
专业化的参数。
由于您似乎仅支持char
,因此@Jarod42's answer中的解决方案是可行的方法。