有没有办法检测函数是否存在并且可以在编译时使用?

时间:2012-06-09 02:30:54

标签: c++ templates metaprogramming

编辑:我的问题的简短回答是,我对SFINAE可以做什么有错误的看法,而且根本没有检查功能体:does sfinae instantiates a function body?

我遇到与此类似的问题:Is it possible to write a template to check for a function's existence?

不同之处在于我不仅要检查函数是否存在,而且还想知道它是否真的会通过SFINAE。以下是我要完成的一个例子:

struct A
{
    void FuncA() { std::cout << "A::FuncA" << std::endl; }
};

struct B
{
    void FuncA() { std::cout << "B::FuncA" << std::endl; }
    void FuncB() { std::cout << "B::FuncB" << std::endl; }
};

template<typename T>
struct Inter
{
    void FuncA() { t.FuncA(); }
    void FuncB() { t.FuncB(); }

    T t;
};

// Always takes some sort of Inter<T>.
template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        // if( t.FuncA() exists and can be called )
            t.FuncA();

        // if( t.FuncB() exists and can be called )
            t.FuncB();
    }

    InterType t;
};

void DoEverything()
{
    Final<Inter<A>> finalA;
    Final<Inter<B>> finalB;

    finalA.CallFuncs();
    finalB.CallFuncs();
}

请注意,在CallFuncs()中,FuncA()和FuncB()都将始终存在,但它们可能无法编译,具体取决于Inter中使用的类型T.当我试图在上面的链接问题中使用答案时,它似乎总是给我真实,我猜是因为它只是检查函数是否存在,而不是它实际上可以被编译(尽管我不能排除我没有把事搞砸了......)

为了有条件地调用我认为的函数,我可以使用enable_if:

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncA can be called? */>::type TryCallFuncA( InterType& i )
{
    i.FuncA();
}

template<typename InterType>
typename std::enable_if< ! /* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
}
template<typename InterType>
typename std::enable_if</* how to determine if FuncB can be called? */>::type TryCallFuncB( InterType& i )
{
    i.FuncB();
}

template<typename InterType>
struct Final
{
    void CallFuncs()
    {
        TryCallFuncA(t);
        TryCallFuncB(t);
    }

    InterType t;
};

但我不确定是否有任何方法可以将布尔值传递给enable_if。有什么方法可以实现这一点,还是我需要回到某种手动维护的类型特征来指示函数是否存在?

就可用的C ++ 11功能集而言,它是值得的,我正在使用MSVC 2010。

编辑:要添加一个重要的注释,在我的实际情况中,类Inter的实现实际上是不透明的,我需要确定Inter :: FuncA / FuncB是否会编译所以我不能只是冒出子类型并检查它们是否存在。

1 个答案:

答案 0 :(得分:8)

我现在没时间检查,但您可以添加Finaltemplate <typename T> struct Final< Inner<T> >;的特化(这也有助于确保类型始终为Inner。有了它,您可以提取用于实例化Inter的类型。

现在第二个问题是如何使用SFINAE来检测成员函数是否存在。我认为这不应该太复杂(如果你不需要这个通用):

// Find out whether U has `void f()` member
template <typename U>
struct has_member_f {
    typedef char yes;
    struct no { char _[2]; };
    template<typename T, void (T::*)() = &T::f>
    static yes impl( T* );
    static no  impl(...);

    enum { value = sizeof( impl( static_cast<U*>(0) ) ) == sizeof(yes) };
};

您可能能够对此进行一些扩展以使其更通用,但我认为您不能通用的函数的名称。当然,您可以将其编写为生成has_member_##arg并使用&T:: arg的宏。成员的类型可能更容易概括......

或者,由于我不认为这可以是通用的,你可以直接在你的类型中使用has_member内的技巧:提供两个callFuncA重载,一个用可选的第二个参数模板化你想要的签名和默认为转发呼叫的&T::FuncA,另一个是带有省略号的省略号。然后callFuncs会拨打callFuncAcallFuncB,SFINAE会发送到转发器或中午,您就可以获得所需的行为。

template<typename T>
struct Final< Inter<T> >
{
    template <typename U, void (U::*)() = &U::FuncA>
    void callFuncA( Inter<T>* x ) {
        x.FuncA();
    }
    void callFuncA(...) {}

    void CallFuncs() {
        callFuncA(&t);                 // Cannot pass nonPOD types through ...
        // Similarly TryCallFuncB(t);
    }
    Inter<T> t;
};