仅当返回表达式有效时才启用模板

Enable template only if the return expression is valid

本文关键字:启用 有效 返回 表达式      更新时间:2023-10-16

我想用这种风格在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_ifstd::is_invocablestd::result_of的更晦涩的条件,但它们引入了许多我无法理解的错误,在这里总结所有尝试可能毫无意义。

有没有办法正确地做这件事?可能与 C++14 一起使用,因此代码库仍然更向后兼容,但如果 C++17 是必需的,也可以。

您可以将重载添加到强制类型(因此选择唯一的可用重载(:

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;
};

演示

std::endl没有重载。这是一个声明如下的函数模板:

template< class CharT, class Traits >
std::basic_ostream<CharT, Traits>& endl( std::basic_ostream<CharT, Traits>& os );

它直接适用于std::ostream的原因是适当的operator <<(流操纵器的(是一个常规成员函数(尽管是从charbasic_ostream模板生成的(。它期望混凝土机械手类型。模板参数推导可以与此参数类型一起使用,以推导出正确endl专用化的参数。

由于您似乎只支持std::ostream,因此@Jarod42答案中的解决方案是要走的路。