函数与函数名之间有一个&符号

SFINAE with ampersand before a function vs its name

本文关键字:函数 符号 有一个 之间      更新时间:2023-10-16

下面的代码正确地检查类型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;
}

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

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将是一个函数指针

通过使用&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;