我发现这个类型特征可以用来检查某种类型 T
支持operator<<
:
template<class Class>
struct has_ostream_operator_impl {
template<class V>
static auto test(V*) -> decltype(std::declval<std::ostream>() << std::declval<V>());
template<typename>
static auto test(...) -> std::false_type;
using type = typename std::is_same<std::ostream&, decltype(test<Class>(0))>::type;
};
template<class Class>
struct has_ostream_operator : has_ostream_operator_impl<Class>::type {};
来源:https://gist.github.com/szymek156/9b1b90fe474277be4641e9ef4666f472
这很好用。现在我正在尝试对 operator>>
做同样的事情使用 c++11,但我无法让它工作:
template<class Class>
struct has_istream_operator_impl {
template<class V>
static auto test(V*) -> decltype(std::declval<V>() >> std::declval<std::istream>());
template<typename>
static auto test(...) -> std::false_type;
using type = typename std::is_same<std::istream&, decltype(test<Class>(0))>::type;
};
/**
* @brief Type trait to check if operator>>(std::istream, Type) is defined for a given type.
*/
template<class Class>
struct has_istream_operator : has_istream_operator_impl<Class>::type {};
这是我的用例的简化测试:
#include <sstream>
#include <type_traits>
#include <iostream>
// <include snippet 2>
template<typename T>
typename std::enable_if<has_istream_operator<T>::value, T>::type
fromString(const std::string& str) {
T value;
std::istringstream stream(str);
stream >> value;
return value;
}
int main() {
std::cout << fromString<long>("123") + 1 << std::endl; // expecting 124
return 0;
}
错误是:
has_istream_operator.cpp: In function ‘int main()’:
has_istream_operator.cpp:57:38: error: no matching function for call to ‘fromString<long int>(const char [4])’
std::cout << fromString<long>("123") + 1 << std::endl; // expecting 124
^
has_istream_operator.cpp:49:1: note: candidate: ‘template<class T> typename std::enable_if<has_istream_operator<Class>::value, T>::type fromString(const string&)’
fromString(const std::string& str) {
^~~~~~~~~~
has_istream_operator.cpp:49:1: note: template argument deduction/substitution failed:
has_istream_operator.cpp: In substitution of ‘template<class T> typename std::enable_if<has_istream_operator<Class>::value, T>::type fromString(const string&) [with T = long int]’:
has_istream_operator.cpp:57:38: required from here
has_istream_operator.cpp:49:1: error: no type named ‘type’ in ‘struct std::enable_if<false, long int>’
从中我了解到 SFINAE 条件为假,因此没有 fromString
的定义存在。
我尝试过的是玩弄线条
static auto test(V*) -> decltype(std::declval<V>() >> std::declval<std::istream>());
在我对 struct has_istream_operator_impl
的定义中.
这是对我来说最有意义的变体,因为当我使用运算符>>时,我通常这样做:stream >> value
例如,根据我的(有限)理解,test(V*)
应该对 V
进行一般测试:
static auto test(V*) -> decltype(std::declval<std::istream>() >> std::declval<V>());
但它也不起作用(同样的错误)。
如何让它发挥作用?
最佳答案
长话短说,你应该改变
static auto test(V*) -> decltype(std::declval<V>() >> std::declval<std::istream>());
至
static auto test(V*) -> decltype(std::declval<std::istream>() >> std::declval<V&>());
由于以下原因,代码中存在两个错误。
>>
运算符将流作为第一个参数,而不是第二个参数,而您以相反的方式传递两个参数。-
declval<V>()
正在生成一个右值(嗯,不是真正的值,因为我们处于未评估的上下文中),您无法通过>>
为其分配值(就像你不能cin >> 123;
),所以你必须将其更改为declval<V&>()
.(1)
1 要更深入地了解为什么会出现这种情况,请查看 std::declval
的可能实现如the documentation page on cppreference所示:如您所见,它返回类型 typename std::add_rvalue_reference<T>::type
(顺便说一句,可以写成 std::add_rvalue_reference_t<T>
since C++14 ),即 std::declval<T>()
返回T&&
,这是(通过引用折叠)
- 左值引用(如果您提供左值引用)
T
至std::declval
(例如std::declval<int&>()
), - 否则为右值引用(例如
std::declval<int>()
)。
在您的用例中,您正在传递 long
作为T
至std::declval
,所以我们处于第二种情况,即 std::declval<long>()
返回long&&
。从页面 value categories您可以看到 xvalue(就像纯右值一样,右值)的示例如下(我的重点):
a function call or an overloaded operator expression, whose return type is rvalue reference to object, such as std::move(x);
这正是std::decltype<long>()
是:它有一个对对象的右值引用作为其返回类型,因此它返回一个右值。
如果您调用,std::decltype<T&>()
并通过 long
如T
,调用将是 std::decltype<long&>()
,在这种情况下,返回类型将为 long&
,因此调用将返回一个左值。请参阅同一页面中以下引用的左值示例
a function call or an overloaded operator expression, whose return type is lvalue reference;
举个例子std::decltype
正在做,这些都通过了
static_assert(std::is_same_v<decltype(std::declval<int>()), int&&>);
static_assert(std::is_same_v<decltype(std::declval<int&>()), int&>);
编译失败
int x;
std::cin >> static_cast<int&&>(x); // XXX
其中 // XXX
线路是什么std::declval<std::istream>() >> std::declval<V>()
是“模仿”。
关于c++ - 用于检查给定类型是否存在 istream 运算符 >> 的类型特征,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73386231/