9

可能重复:
是否可以编写 C++ 模板来检查函数是否存在?

我有一个f接收val类型值T(模板化)的函数。val 只有当类型具有这样的成员函数时,有没有办法调用成员函数?

例子:

struct Bar {
  void foo() const {}
};

template<class T>
void f(T const& val) {
  // Is there any way to call *only* if foo() is available on type T?                                                           
  // SFINAE technique?                                                                                                          
  val.foo();
}

int main() {
  Bar bar;
  f(bar);
  f(3.14);
}

对我来说听起来像是 SFINAE 技术,可能使用 boost::enable_if,但我不知道如何让它在这里工作。请注意,我不能轻易更改Bar示例中的类型。我知道如果Bar包含一些特定的 typedef 等,这会很容易,这表明该功能是可用的。

不用说,我不知道将被调用的类型Tf。有些有foo成员函数,有些没有。

4

2 回答 2

7

您可以按照下面的测试程序(使用 GCC 4.7.0 或 clang 3.1 构建)进行说明。

如果该方法存在并且是公共的,has_void_foo_no_args_const<T>::eval(T const & t) 则将调用静态模板函数。如果没有这样的方法,它什么也做不了。(如果方法是私有的,当然会导致编译错误。)t.foo()void T::foo() const

这个解决方案是从我在这里贡献的方法内省模板改编和扩展的。您可以阅读该答案以了解 SNIFAE 逻辑的工作原理,并了解如何推广该技术以参数化您正在探测的函数签名的属性。

#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;
}

该程序输出:

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
于 2012-08-22T09:10:31.200 回答
0

如果你有一个typedef bool has_f, 或一个 static (在编译时可用的东西),我想你可以分支(使用 boost 元编程函数),或者提供 2 个模板,一个将实例化如果has_f被定义,另一个如果不是。如果没有关于T恕我直言的元数据,直接感觉是不可行的。

于 2012-08-18T02:26:57.750 回答