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

olp*_*lpa 9 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;
}
Run Code Online (Sandbox Code Playgroud)

Gui*_*cot 5

答案很简单:如果没有成员函数的地址,就不能使用&.你不能自己尝试:

auto fun = Foo::sort; // error
Run Code Online (Sandbox Code Playgroud)

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

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

因此,sfinae检查是正确的:没有&,如果类型T将具有名为的静态数据成员,则检查将为true sort.

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

struct Foo {
    void sortImpl();

    static constexpr auto sort = &Foo::sortImpl;
};
Run Code Online (Sandbox Code Playgroud)

然后检查名为的静态数据成员sort是否正确,并且sort将是一个函数指针.


sky*_*ack 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()); // (*)
Run Code Online (Sandbox Code Playgroud)

这样,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);
};
Run Code Online (Sandbox Code Playgroud)

如果可以使用 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;
Run Code Online (Sandbox Code Playgroud)

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

std::cout << "Foo: " << has_sort<Foo> << std::endl;
Run Code Online (Sandbox Code Playgroud)