我想编写一个类型特征,它使用 SFINAE 来检查类型是否存在下标表达式。我在下面的初步尝试似乎在下标表达式可用时有效,但在括号运算符不存在时不起作用。

#include <iostream>
#include <vector>
#include <cassert>

template<class T, class Index>
struct has_subscript_operator_impl
{
  template<class T1,
           class Reference = decltype(
             (*std::declval<T*>())[std::declval<Index>()]
           ),
           class = typename std::enable_if<
             !std::is_void<Reference>::value
           >::type>
  static std::true_type test(int);

  template<class>
  static std::false_type test(...);

  using type = decltype(test<T>(0));
};


template<class T, class Index>
using has_subscript_operator = typename has_subscript_operator_impl<T,Index>::type;

struct doesnt_have_it {};

struct returns_void
{
  void operator[](int) {}
};

struct returns_int
{
  int operator[](int) { return 0; }
};

int main()
{
  std::cout << "has_subscript_operator<doesnt_have_it,int>: " << has_subscript_operator<doesnt_have_it,int>::value << std::endl;
  assert((!has_subscript_operator<doesnt_have_it,int>::value));

  std::cout << "has_subscript_operator<returns_void,int>: " << has_subscript_operator<returns_void,int>::value << std::endl;
  assert((!has_subscript_operator<returns_void,int>::value));

  std::cout << "has_subscript_operator<returns_int,int>: " << has_subscript_operator<returns_int,int>::value << std::endl;
  assert((has_subscript_operator<returns_int,int>::value));

  std::cout << "has_subscript_operator<int*,int>: " << has_subscript_operator<int*,int>::value << std::endl;
  assert((has_subscript_operator<int*,int>::value));

  std::cout << "has_subscript_operator<std::vector<int>,int>: " << has_subscript_operator<std::vector<int>,int>::value << std::endl;
  assert((has_subscript_operator<returns_int,int>::value));

  return 0;
}
clang-3.4 的输出:
$ clang -std=c++11 -I. -lstdc++ test_has_subscript_operator.cpp
test_has_subscript_operator.cpp:10:14: error: type 'doesnt_have_it' does not provide a subscript operator
             (*std::declval<T*>())[std::declval<Index>()]
             ^~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
test_has_subscript_operator.cpp:25:1: note: in instantiation of template class 'has_subscript_operator_impl<doesnt_have_it, int>' requested here
using has_subscript_operator = typename has_subscript_operator_impl<T,Index>::type;
^
test_has_subscript_operator.cpp:41:66: note: in instantiation of template type alias 'has_subscript_operator' requested here
  std::cout << "has_subscript_operator<doesnt_have_it,int>: " << has_subscript_operator<doesnt_have_it,int>::value << std::endl;
                                                                 ^
1 error generated.

如何修复我的 has_subscript_operator 使其适用于所有类型?

最佳答案

SFINAE 仅在直接上下文中发生替换失败时才起作用。模板参数 Index 在成员函数模板 test 被实例化时已经知道了,所以你会得到一个硬错误而不是替换失败。

解决这个问题的技巧是通过向 Index 添加一个额外的模板类型参数并将其默认为 test 再次推导出 Index

template<class T1,
       class IndexDeduced = Index,  // <--- here
       class Reference = decltype(
         (*std::declval<T*>())[std::declval<IndexDeduced>()] // and use that here
       ),
       class = typename std::enable_if<
         !std::is_void<Reference>::value
       >::type>
static std::true_type test(int);

现在您的代码按预期工作。

Live demo

关于c++ - 如何检查下标运算符的存在?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31305894/

10-17 00:30