SFINAE在功能与名称之前带有&符号

时间:2016-10-01 21:01:23

标签: c++ templates c++11

以下代码正确检查类型T是否有方法sort。但是,当我通过将(*)更改为decltype(&U::sort,...)(符号decltype(U::sort,...)已删除)来修改标记为&的行时,代码将始终返回false

为什么?

为什么名称本身不够?这&是什么意思?

#include <iostream>
#include <type_traits>

template <typename T>
class has_sort {

  template <typename U>
  static auto check(bool) -> decltype(&U::sort, std::true_type()); // (*)

  template <typename U>
  static std::false_type check(...);

public:
  using type = decltype(check<T>(true));
  static bool const value = type::value;
};

int main() {
  struct Foo { void sort(); };
  struct Foo2 { void sort2(); };
  std::cout << "Foo: " << has_sort<Foo>::value << std::endl;
  std::cout << "Foo2: " << has_sort<Foo2>::value << std::endl;
  std::cout << "int: " << has_sort<int>::value << std::endl;
}

2 个答案:

答案 0 :(得分:5)

答案很简单:如果没有&,您就无法获取成员函数的地址。你不能自己尝试:

auto fun = Foo::sort; // error

标准要求成员函数指针必须与&一起使用,因为没有它,语法将是不明确的。想象一下,在模板中:

template<typename T>
void test() {
    T::test2; // is it a member function pointer or static data member?
}

所以sfinae检查是正确的:没有&,如果类型T具有名为sort的静态数据成员,则检查将为真。

然而,你可以通过这个技巧绕过这个限制,但是,它是出于演示的目的,我不建议你这样做:

struct Foo {
    void sortImpl();

    static constexpr auto sort = &Foo::sortImpl;
};

然后检查名为sort的静态数据成员是否正确,sort将是函数指针。

答案 1 :(得分:3)

通过使用&U::foo,您通常会检查类型U是否包含成员方法(静态或非静态)或数据成员(静态或非静态)。
因此,它将匹配以下所有类型(以及其他类型,如果您还考虑说明符):

  • struct Foo { void sort(); };
  • struct Foo { static void sort(); };
  • struct Foo { int sort; };
  • struct Foo { static int sort; };

另一方面,U::foo不能用于检测成员方法(即使在某些情况下仍然可以使用它来检测数据成员)。
无论如何,因为您还可以使用template <typename U> static std::false_type check(...);,当您尝试检测sort成员方法时,上述函数的特化期间的错误会因为sfinae规则而被默默地丢弃,并且这个被选中起来。

如果您想要更加严格并且要求sort成为一个函数(无论是否为静态函数),您应该包含utility标题并改为使用std:: declval。这样,不再需要&符号:

template <typename U>
static auto check(bool) -> decltype(std::declval<U>().sort(), std::true_type()); // (*)

这样,数据成员名称sort将不再被检测到。

如果我可以给你一个建议,你可以使用int / char重载和constexpr功能来简化一些事情。
举个例子:

template <typename T>
class has_sort {
    template <typename U>
    constexpr static auto check(int) -> decltype(std::declval<U>().sort(), std::true_type()) { return {}; }

    template <typename U>
    constexpr static std::false_type check(char) { return {}; }

public:
    static constexpr bool value = check<T>(0);
};

如果你可以使用C ++ 14,模板变量就更紧凑了:

template<typename T, typename = void>
constexpr bool has_sort = false;

template<typename T>
constexpr bool has_sort<T, decltype(std::declval<T>().sort(), void())> = true;

您可以在示例中使用它,如下所示:

std::cout << "Foo: " << has_sort<Foo> << std::endl;