开发者

Checking whether a non-hardwired member function exists with SFINAE

I want to create proxies for member functions and operators. They must have the same return type and parameters, and must be good for several classes, which are given as template parameters. Even if the class does not have the particular member function or operator, I want it to compile instead of failing with an error, essentially SFINAE. If X has a method f() and Y does not have any method named f, I need Proxy<X> to have an f() as well that calls X::f(), and I need Proxy<Y> to compile and instantiate without any problems.

Extracting the return type from a known function is no longer a problem, after a previous question of mine. However it fails with an error if there is no such function.

I already know several template metaprogramming tricks to determine whether a given function exists, and enable a certain feature if they do, however, they all work only on hardwired function names instead of arbitrary ones, which severely limits their use in this case since I need the same construct for several functions.

I only need to check whether any function with the given name exists, if there are overloaded variants I do not need to check if a specific one exists, automatic template deduction solves that (or so I hope)

My current code looks like this:

template <class T>
class Proxy
{

    //  using my resultof solution
    template <class... Args>
    resultof(T::f, Args...) f (Args... x)
    {
        return x.f(x...);
    }

    //  using another return type extraction solution
    template <class... Args>
    typeof(T::f(std::declval<Args>()...)) f (Args... x)
    {
        return x.f(x...);
    }

    T x;

};

Which should compile even if T does not have any function named f. Unfortunately both version fail with an error.

The implementation of resultof being

#define resultof(f, ...) typeof(Param<__VA_ARGS__>::Func(f))

template <class... Args>
class Param
{

    public:

        template <class R>
        static R Func (R (*) (Args...));

        templa开发者_运维技巧te <class R, class C>
        static R Func (R (C::*) (Args...));

        template <class R, class C>
        static R Func (R (C::*) (Args...) const);

};


I suspect that

template<typename... Args>
decltype( std::declval<T&>().f(std::declval<Args>()...) )
f(Args&&... args)
{
    return x.f(std::forward<Args>(args)...);
}

should trigger SFINAE and discard any instantiation of f for which the return type is ill-formed (e.g. ambiguous or non-existant overload) instead of a hard error. I'm not quite sure though because T is a parameter of proxy, not f and I simply can't parse the relevant parts of the Standard (around 14.8.2 I believe). None of the examples provided in the non normative notes seems to apply.

Failing that, it's possible to use

template<typename U = T&, typename... Args>
decltype( std::declval<U>().f(std::declval<Args>()...) )
f(Args&&... args)
{
    return x.f(std::forward<Args>(args)...);
}

for which my compiler happily accepts proxy<int> p;, unlike with the first option. p.f(); results in a 'No matching function found' error, as is usual with SFINAE.


I recommend using the freestanding form of the operators where possible:

template<typename T, typename U>
auto operator+(Proxy<T> const& lhs, Proxy<U> const& rhs)
-> decltype( std::declval<T const&>() + std::declval<U const&>() )
{
    return lhs.x + rhs.x;
}

is a possibility.


At first glance, this seems trivial:

template <typename T> class Proxy : public T { };

Nothing else in C++ will give Proxy<T> all the members of T, for any T. The only bit missing is the ctors, but from your question I infer that you already know how to forward those.

Background: Practically speaking, the set of possible member names of T is infinite. Therefore, you can't find .f() by name lookup in Proxy<T>, and the only other scope in which a member name is looked up is the base class scope.


You need to isolate the checking of the existence of f in the template parameter of proxy by an extra level. The following will allow you to call proxy<X>::f() in any way that you can call X::f():

template<typename T,typename ... Args>
struct f_result
{
    typedef decltype(std::declval<T&>().f(std::declval<Args&&>()...)) type;
};

template<typename T>
struct proxy
{
    T val;

    template<typename ... Args>
    typename f_result<T,Args...>::type
    f(Args&& ... args)
    {
        return val.f(static_cast<Args&&>(args)...);
    }
};

Quick test:

#include <iostream>

struct X
{
    void f()
    {
        std::cout<<"X::f()"<<std::endl;
    }

    int f(int i)
    {
        std::cout<<"X::f("<<i<<")"<<std::endl;
        return i;
    }
};

struct Y
{};

struct Z
{
    int f()
    {
        std::cout<<"Z::f()"<<std::endl;
        return 42;
    }
};

int main(int, char**)
{
    proxy<X> px;
    px.f();
    int i=px.f(3);
    std::cout<<"i="<<i<<std::endl;

    proxy<Y> py;
    proxy<Z> pz;
    int j=pz.f();
    std::cout<<"j="<<j<<std::endl;
}

This works OK with g++ 4.5 and g++ 4.6 in -std=c++0x mode.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜