为什么我必须调用 operator<< 作为 SFINAE 与 void_t 一起工作的方法?
Why do I have to call operator<< as a method for SFINAE to work with void_t?
我正在尝试定义一个 has_ostream_operator<T>
SFINAE 测试来检查我是否可以计算出给定的类型。我让它工作,但前提是在我对 has_ostream_operator
的定义中,我将 operator<<
称为方法而不是中缀运算符。换句话说,这有效:
decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>
这不是:
decltype(std::declval<std::ostream>() << std::declval<T>())>
下面的测试用例(也可以在http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9看到)。请注意,我包含了 void_t 的定义,因为我只使用 C++14。
#include <iostream>
namespace std {
template<class...>
using void_t = void;
}
template<class, class = std::void_t<>>
struct has_ostream_operator : std::false_type {};
template<class T>
struct has_ostream_operator<
T,
std::void_t<
decltype(
std::declval<std::ostream>().operator<<(std::declval<T>()))>>
: std::true_type {};
struct Foo {};
template<class X>
void print(
const X& x,
std::enable_if_t<has_ostream_operator<X>::value>* = 0)
{
std::cout << x;
}
template<class X>
void print(
const X&,
std::enable_if_t<!has_ostream_operator<X>::value>* = 0)
{
std::cout << "(no ostream operator<< implementation)";
}
int main()
{
print(3); // works fine
print(Foo()); // this errors when using infix operator version
return 0;
}
你需要
std::declval<std::ostream&>() << std::declval<T>()
// ^
std::declval<std::ostream>()
是右值;您正在为右值流击中包罗万象的 operator<<
重载。
如果使用中缀表示法,则会找到右值流插入器,因为 declval
returns 右值本身; [ostream.rvalue]:
template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);
此重载当前接受 x
的所有参数。我已提交 LWG #2534,如果得到相应解决,将使您的初始代码按预期工作。
临时解决方法是使 declval
return 成为左值引用,即将模板参数调整为一个:
std::declval<std::ostream&>() << std::declval<T>()
我假设您的 "infix" 版本使用了这个表达式:
std::declval<std::ostream>() << std::declval<T>()
匹配 Foo
的原因是因为第一部分 declval<ostream>()
产生类型 ostream&&
的右值。这匹配一个非成员 operator<<
:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
该重载只是转发呼叫:
Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to os << value
).
您应该直接检查。所有的重载都采用 ostream
by lvalue 引用,所以你也应该测试一下:
std::declval<std::ostream&>() << std::declval<T>()
我正在尝试定义一个 has_ostream_operator<T>
SFINAE 测试来检查我是否可以计算出给定的类型。我让它工作,但前提是在我对 has_ostream_operator
的定义中,我将 operator<<
称为方法而不是中缀运算符。换句话说,这有效:
decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>
这不是:
decltype(std::declval<std::ostream>() << std::declval<T>())>
下面的测试用例(也可以在http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9看到)。请注意,我包含了 void_t 的定义,因为我只使用 C++14。
#include <iostream>
namespace std {
template<class...>
using void_t = void;
}
template<class, class = std::void_t<>>
struct has_ostream_operator : std::false_type {};
template<class T>
struct has_ostream_operator<
T,
std::void_t<
decltype(
std::declval<std::ostream>().operator<<(std::declval<T>()))>>
: std::true_type {};
struct Foo {};
template<class X>
void print(
const X& x,
std::enable_if_t<has_ostream_operator<X>::value>* = 0)
{
std::cout << x;
}
template<class X>
void print(
const X&,
std::enable_if_t<!has_ostream_operator<X>::value>* = 0)
{
std::cout << "(no ostream operator<< implementation)";
}
int main()
{
print(3); // works fine
print(Foo()); // this errors when using infix operator version
return 0;
}
你需要
std::declval<std::ostream&>() << std::declval<T>()
// ^
std::declval<std::ostream>()
是右值;您正在为右值流击中包罗万象的 operator<<
重载。
如果使用中缀表示法,则会找到右值流插入器,因为 declval
returns 右值本身; [ostream.rvalue]:
template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);
此重载当前接受 x
的所有参数。我已提交 LWG #2534,如果得到相应解决,将使您的初始代码按预期工作。
临时解决方法是使 declval
return 成为左值引用,即将模板参数调整为一个:
std::declval<std::ostream&>() << std::declval<T>()
我假设您的 "infix" 版本使用了这个表达式:
std::declval<std::ostream>() << std::declval<T>()
匹配 Foo
的原因是因为第一部分 declval<ostream>()
产生类型 ostream&&
的右值。这匹配一个非成员 operator<<
:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
该重载只是转发呼叫:
Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to
os << value
).
您应该直接检查。所有的重载都采用 ostream
by lvalue 引用,所以你也应该测试一下:
std::declval<std::ostream&>() << std::declval<T>()