仅当 return 表达式有效时启用模板
Enable template only if the return expression is valid
我想用这种风格在 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 可以解决这种情况,但我找不到有效的方法。我想我需要类似 "enable this template only when cout << arg
is a well formed expression" 的东西,但我不知道如何表达。
例如,我试过这个:
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,也可以。
您可以添加重载以强制类型(因此选择了唯一可用的重载):
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 <<
(用于流操纵器的那个)是一个常规成员函数(尽管从 basic_ostream
模板生成 char
).它需要一个具体的操纵器类型。模板参数推导可与此参数类型一起使用,以推导正确 endl
专业化的参数。
由于您似乎只支持 std::ostream
, 中的解决方案是可行的方法。
我想用这种风格在 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 可以解决这种情况,但我找不到有效的方法。我想我需要类似 "enable this template only when cout << arg
is a well formed expression" 的东西,但我不知道如何表达。
例如,我试过这个:
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,也可以。
您可以添加重载以强制类型(因此选择了唯一可用的重载):
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 <<
(用于流操纵器的那个)是一个常规成员函数(尽管从 basic_ostream
模板生成 char
).它需要一个具体的操纵器类型。模板参数推导可与此参数类型一起使用,以推导正确 endl
专业化的参数。
由于您似乎只支持 std::ostream
,