[英]Why do I have to call operator<< as a method for SFINAE to work with void_t?
I am trying to define a has_ostream_operator<T>
SFINAE test for checking whether I can cout a given type. 我正在尝试定义一个
has_ostream_operator<T>
SFINAE测试,以检查我是否可以输出给定类型。 I have it working, but only if in my definition of has_ostream_operator
I call operator<<
as a method rather than as an infix operator. 我有它工作,但只有在我的
has_ostream_operator
定义中,我将operator<<
作为方法而不是作为中缀运算符。 In other words this works: 换句话说,这有效:
decltype(std::declval<std::ostream>().operator<<(std::declval<T>()))>
This does not: 这不是:
decltype(std::declval<std::ostream>() << std::declval<T>())>
Test case below (can also see at http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9 ). 下面的测试用例(也可以参见http://coliru.stacked-crooked.com/a/d257d9d6e0f3f6d9 )。 Note that I included a definition of void_t since I'm only on C++14.
请注意,我包含了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;
}
I'm assuming your "infix" version used this expression: 我假设你的“中缀”版本使用了这个表达式:
std::declval<std::ostream>() << std::declval<T>()
The reason that matches for Foo
is because the first part, declval<ostream>()
produces an rvalue of type ostream&&
. 匹配
Foo
的原因是因为第一部分, declval<ostream>()
产生一个类型为ostream&&
的右值。 This matches a non-member operator<<
: 这匹配非成员
operator<<
:
template< class CharT, class Traits, class T >
basic_ostream< CharT, Traits >& operator<<( basic_ostream<CharT,Traits>&& os,
const T& value );
That overload simply forwards the call along: 这种过载只会转发呼叫:
Calls the appropriate insertion operator, given an rvalue reference to an output stream object (equivalent to
os << value
).给定对输出流对象的rvalue引用(相当于
os << value
),调用适当的插入运算符。
You should instead check for that directly. 您应该直接检查。 All the overloads take an
ostream
by lvalue reference, so you should test that too: 所有重载都通过左值引用获取
ostream
,因此您也应该测试它:
std::declval<std::ostream&>() << std::declval<T>()
You need 你需要
std::declval<std::ostream&>() << std::declval<T>()
// ^
std::declval<std::ostream>()
is an rvalue; std::declval<std::ostream>()
是一个右值; you are hitting the catch-all operator<<
overload for rvalue streams. 你正在使用catch-all
operator<<
over rvalue streams。
If you use infix notation, the rvalue stream inserter is found, since declval
returns rvalues per se; 如果使用中缀表示法,则会找到右值流插入器,因为
declval
本身declval
返回rvalues; [ostream.rvalue]: [ostream.rvalue]:
template <class charT, class traits, class T>
basic_ostream<charT, traits>& operator<<(basic_ostream<charT, traits>&& os, const T& x);
This overload currently accepts all arguments for x
. 此重载当前接受
x
所有参数。 I've submitted LWG #2534 , which, if resolved accordingly, will make your initial code work as expected. 我已经提交了LWG #2534 ,如果得到相应的解决,将使您的初始代码按预期工作。
A temporary workaround is to make declval
return an lvalue reference, ie adjust the template argument to one: 临时解决方法是使
declval
返回左值引用, declval
模板参数调整为:
std::declval<std::ostream&>() << std::declval<T>()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.