c++ - 用于检查给定类型是否存在 istream 运算符 >> 的类型特征

标签 c++ c++11 templates sfinae declval

我发现这个类型特征可以用来检查某种类型 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&& ,这是(通过引用折叠)

  • 左值引用(如果您提供左值引用)Tstd::declval (例如 std::declval<int&>() ),
  • 否则为右值引用(例如 std::declval<int>() )。

在您的用例中,您正在传递 long作为Tstd::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&>()并通过 longT ,调用将是 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/

相关文章:

templates - Grails g:include标签包含 Controller 调用

c++ - Variadic 模板化构造函数不接受 x 个参数

具有指针、赋值和比较的 C++ 结构

c++ - 使用 ispunct ('ø' 时调试断言失败)

c++ - 如何 spsc_queue.pop() 这个结构?

c++ - 如何编写一个可以下载并安装许多包的程序(c++)?

c++ - max_bucket_count 函数的意外行为

c++ - 使用来自外部类的可变参数模板的 args 部分专门化可变参数模板内部类是否合法

C++11 lambda 捕获列表引用

ruby-on-rails - Rails 3.1 共享客户端/服务器模板,支持 i18n