输入特征来检查给定类型是否存在 istream 运算符>>

问题描述 投票:0回答:1

我发现这个类型特征可以用来检查某种类型

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

这个效果很好。现在我尝试使用 c++11 对

operator>>
做同样的事情,但我没有让它工作:

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

但它也不起作用(同样的错误)。

如何让它发挥作用?

c++ c++11 templates sfinae declval
1个回答
3
投票

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;
    。(1)
    
    
1 要更深入地了解为什么会出现这种情况,请查看
declval<V&>()

的可能实现,如

cppreference 上的文档页面所示:如您所见,它返回类型 
std::declval(顺便说一句,它可以写为 typename std::add_rvalue_reference<T>::type
 
since C++14
),即 
std::add_rvalue_reference_t<T> 返回 std::declval<T>()
,即(通过引用折叠)
左值引用,如果您向 

T&&
    提供左值引用
  • T
    (例如 
    std::declval
    ),
    否则为右值引用(例如 
    std::declval<int&>()
  • )。
  • 在您的用例中,您将 
  • std::declval<int>()
作为

long

 传递给 
T
,所以我们处于第二种情况,即 
std::declval
 返回 
std::declval<long>()
。从
值类别
页面,您可以看到 xvalue(就像纯右值,右值一样)的示例如下(我的
强调):

函数调用
或重载运算符

表达式,其返回类型是对象的右值引用,例如std::move(x); 这正是

long&&
的含义:它有一个对 object 的右值引用作为其返回类型,因此它返回一个右值。

如果您调用 
std::declval<long>()

并将

std::declval<T&>()

 作为 
long
 传递,则调用将为 
T
,在这种情况下,返回类型将为 
std::declval<long&>()
,因此调用将返回左值。请参阅同一页面中以下引用的左值示例

函数调用
或重载运算符表达式,

其返回类型为左值引用; 举一个

long&
正在做什么的例子,这些都通过了

std::declval

这无法编译
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

 正在“模拟”。

© www.soinside.com 2019 - 2024. All rights reserved.