Checking whether a non-hardwired member function exists with SFINAE

半城伤御伤魂 提交于 2019-12-04 20:09:03

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.

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