How to call member function only if object happens to have it? [duplicate]

久未见 提交于 2019-11-29 00:27:23
Mike Kinghan

You can do this as illustrated by the test program below (built with GCC 4.7.0 or clang 3.1).

The static template function has_void_foo_no_args_const<T>::eval(T const & t) will invoke t.foo() if the method void T::foo() const exists and is public. It will do nothing if there is no such method. (And of course a compile error will result if the method is private.)

This solution is adapted and extended from the method-introspecting template I contributed here. You can read that answer for an explanation of how the SNIFAE logic works, and also to see how the technique might be generalised to parameterize the properties of the function-signature you are probing.

#include <iostream>

/*! The template `has_void_foo_no_args_const<T>` exports a
    boolean constant `value` that is true iff `T` provides
    `void foo() const`

    It also provides `static void eval(T const & t)`, which
    invokes void `T::foo() const` upon `t` if such a public member
    function exists and is a no-op if there is no such member.
*/ 
template< typename T>
struct has_void_foo_no_args_const
{
    /* SFINAE foo-has-correct-sig :) */
    template<typename A>
    static std::true_type test(void (A::*)() const) {
        return std::true_type();
    }

    /* SFINAE foo-exists :) */
    template <typename A> 
    static decltype(test(&A::foo)) 
    test(decltype(&A::foo),void *) {
        /* foo exists. What about sig? */
        typedef decltype(test(&A::foo)) return_type; 
        return return_type();
    }

    /* SFINAE game over :( */
    template<typename A>
    static std::false_type test(...) {
        return std::false_type(); 
    }

    /* This will be either `std::true_type` or `std::false_type` */
    typedef decltype(test<T>(0,0)) type;

    static const bool value = type::value; /* Which is it? */

    /*  `eval(T const &,std::true_type)` 
        delegates to `T::foo()` when `type` == `std::true_type`
    */
    static void eval(T const & t, std::true_type) {
        t.foo();
    }
    /* `eval(...)` is a no-op for otherwise unmatched arguments */ 
    static void eval(...){
        // This output for demo purposes. Delete
        std::cout << "T::foo() not called" << std::endl;        
    }

    /* `eval(T const & t)` delegates to :-
        - `eval(t,type()` when `type` == `std::true_type`
        - `eval(...)` otherwise
    */  
    static void eval(T const & t) {
        eval(t,type());
    }
};

// For testing
struct AA {
    void foo() const {
        std::cout << "AA::foo() called" << std::endl;
    }
};

// For testing
struct BB {
    void foo() {
        std::cout << "BB::foo() called" << std::endl;
    }
};

// For testing
struct CC {
    int foo() const {
        std::cout << "CC::foo() called" << std::endl;
        return 0;
    }
};

// This is the desired implementation of `void f(T const& val)` 
template<class T>
void f(T const& val) {
    has_void_foo_no_args_const<T>::eval(val);
}

int main() {
    AA aa;
    std::cout << (has_void_foo_no_args_const<AA>::value ? 
        "AA has void foo() const" : "AA does not have void foo() const")
        << std::endl; 
    f(aa);
    BB bb;  
    std::cout << (has_void_foo_no_args_const<BB>::value ? 
        "BB has void foo() const" : "BB does not have void foo() const")
        << std::endl;
    f(bb);
    CC cc;  
    std::cout << (has_void_foo_no_args_const<CC>::value ? 
        "CC has void foo() const" : "CC does not have void foo() const")
        << std::endl;
    f(cc);  
    std::cout << (has_void_foo_no_args_const<double>::value ? 
        "Double has void foo() const" : "Double does not have void foo() const")
        << std::endl;
    f(3.14);
            return 0;

}

This program outputs:

AA has void foo() const
AA::foo() called
BB does not have void foo() const
T::foo() not called
CC does not have void foo() const
T::foo() not called
Double does not have void foo() const
T::foo() not called

If you had a typedef bool has_f, or a static (something available at compile time), I guess you can branch (using a boost metaprogramming function), or provide 2 templates, one that would instantiate if has_f is defined the other if it's not. It doesn't feel doable directly, without metadata about T, IMHO.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!