具有指向成员函数的指针的 SFINAE 类型特征失败

Posted

技术标签:

【中文标题】具有指向成员函数的指针的 SFINAE 类型特征失败【英文标题】:SFINAE type trait with pointer-to-member-function fails 【发布时间】:2021-05-13 23:10:11 【问题描述】:

我正在练习 SFINAE,并希望实现一个类型特征以检查给定类 T 是否包含方法 print()。我有以下两种变体:

// (1)

template <typename T, typename = int>
struct has_print_method : std::false_type ;

template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::true_type ;

template <typename T>
const bool has_print_method_v = has_print_method<T>::value;
// (2)

template <typename T>
struct has_print_method
    template <typename U, typename = void> struct helper : std::false_type;
    template <typename U> struct helper<U, decltype(&U::print)> : std::true_type;
    static const bool value = helper<T, void (T::*)() const>::value;
;

template <typename T>
const bool has_print_method_v = has_print_method<T>::value;

(1) 只检查成员方法print() 的存在,而忽略成员方法的签名。而(2)检查方法的签名,即它需要一个成员方法void print() const

我通过以下方式测试两种变体:

#include <iostream>
#include <type_traits>

// Simple Class A
struct A
    int a;
    public:
      void print() const
;

// (1) or (2) here

template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
void print(T t) 
    t.print();


void print(double x)
    std::cout << x << '\n';



int main() 
    A a;
    print(a);
    print(1.0);  // (*)
    return 0;


使用类型特征 (1) 并使用 clang 12.0 和 std=c++17 标志进行编译可以按预期工作。但是,使用 (2) 代替,我得到了

<source>:28:50: error: member pointer refers into non-class type 'double'
    static const bool value = helper<T, void (T::*)() const>::value;
                                                 ^
<source>:32:33: note: in instantiation of template class 'has_print_method<double>' requested here
const bool has_print_method_v = has_print_method<T>::value;
                                ^
<source>:34:39: note: in instantiation of variable template specialization 'has_print_method_v<double>' requested here
template<typename T, std::enable_if_t<has_print_method_v<T>, bool> = true>
                                      ^
<source>:35:6: note: while substituting prior template arguments into non-type template parameter [with T = double]
void print(T t) 
     ^~~~~~~~~~~~
<source>:47:5: note: while substituting deduced template arguments into function template 'print' [with T = double, $1 = (no value)]
    print(1.0);
    ^
1 error generated.

我在这里缺少什么?您可以在 Godbolt 上找到示例 here。编辑:嗯,我刚刚注意到两个版本都使用 gcc11.1 编译没有错误。奇怪。

【问题讨论】:

【参考方案1】:

首先,从您的错误消息和我自己的测试来看,我认为您的意思是类型特征 (1) 有效而 (2) 无效。在此基础上,这是一个测试匹配函数签名的特征 (1) 版本:

template <typename T, typename = int>
struct has_print_method : std::false_type ;

template <typename T>
struct has_print_method<T, decltype(&T::print, 0)> : std::is_same <decltype(&T::print), void (T::*)() const> ;

template <typename T>
const bool has_print_method_v = has_print_method<T>::value;

Live demo

【讨论】:

你是对的,感谢您的提示(和答案)!

以上是关于具有指向成员函数的指针的 SFINAE 类型特征失败的主要内容,如果未能解决你的问题,请参考以下文章

指向具有私有构造函数的类的类成员的指针

指向不同类的成员函数的指针

具有模板化类成员函数的 SFINAE

C++|详解类成员指针:数据成员指针和成员函数指针及应用场合

是否可以在调用后自动推断指向三元中成员重载函数的指针的类型?

了解功能特征模板的工作原理。特别是指向成员函数的指针是怎么回事