1

如何通过模板技术调用可能存在的成员函数,我不想使用虚方法,因为类是任意的。

例如:

class A {
    void setValue(int value);
};

如何定义一个模板来调用 classA的 setValue 如果它存在,否则什么都不做。

模板类似于:

template <typename T>
struct call_if_exist {
    void invokeOrNot(T& a, int value){ // EXISTING: setValue
        a.setValue(value);
    }
}
template <typename T>
struct call_if_exist {
    void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
    }
}

这是关于调用,而不是检查。

4

1 回答 1

1

您可以利用SFINAE制作一个类模板来检查给定类中是否存在成员函数,并使用结果作为布尔标志来专门针对没有函数的情况以及当成员函数存在:

template<typename T , bool member_exists = has_set_value<T>::result>
struct call_if_exist;

template <typename T>
struct call_if_exist<T,true> {
    void invokeOrNot(T& a, int value){ // EXISTING: setValue
        a.setValue(value);
    }
}
template <typename T>
struct call_if_exist<T,false> {
    void invokeOrNot(T& a, int value){ // NOT EXISTING: do nothing
    }
}

编辑:has_set_value特质

template<typename T>
class has_set_value
{
    typedef struct{ char c[1]; } yes;
    typedef struct{ char c[2]; } no;

    template<typename U> static yes test(&U::set_value);
    template<typename U> static no  test(...);

public:
    static const bool result = sizeof( test<T>(NULL) ) == sizeof( yes );
};

该类是使用 SFINAE 检查某个类的成员(类型或函数)是否存在的典型示例。

首先,我们定义了两个 typedefyesno,它们用于通过运算符区分重载决议sizeof
的第一个重载test()一个指向成员函数的指针作为参数,最后一个是一个重载,其目标是被所有不是指向成员的指针的东西调用。这是通过可变参数函数(注意省略号)完成的,它可以与任何类型的参数一起使用。

实现的重点是即使第二个重载可以保存任何参数,第一个是指向我们的成员函数的指针的显式案例。因此,如果参数可以是指向函数的指针,则调用将解析为第一个函数,否则解析为第二个。

正如我之前所说,我们使用 typedefs 通过sizeof运算符来区分重载决议:注意第一个重载返回yes和后面的返回no所以如果使用指针调用的结果类型的大小test()(NULL)等于 的大小yes,则意味着重载被解析为第一个,并且作为参数(T)传递的类有一个名为的成员函数set_value

Alexandrescu 的 Modern C++ Design在第二章中包含了这种特征的示例,以检查一种类型是否可以隐式转换为另一种类型

于 2013-09-08T10:46:46.097 回答