检查SFINAE是否存在非硬连线成员函数

时间:2011-09-02 05:04:13

标签: c++ templates return-value overloading c++11

我想为成员函数和运算符创建代理。它们必须具有相同的返回类型和参数,并且必须适用于多个类,这些类作为模板参数给出。即使该类没有特定的成员函数或运算符,我也希望它能够编译而不是失败并出现错误,主要是SFINAE。如果X有方法f()Y没有任何名为f的方法,我需要Proxy<X>来获得f()调用X::f(),我需要Proxy<Y>来编译和实例化而没有任何问题。

previous question of mine之后,从已知函数中提取返回类型不再是问题。但是,如果没有这样的功能,它会失败并显示错误。

我已经知道几个模板元编程技巧来确定某个特定函数是否存在,并且如果它们存在,则启用某个特征,但是,它们都只能用于硬连线函数名而不是任意函数名,这严重限制了它们在这种情况下的使用因为我需要几个函数的相同构造。

我只需要检查是否存在具有给定名称的任何函数,如果存在重载变量,我不需要检查是否存在特定的变量,自动模板推导解决了这个问题(或者我希望如此)

我目前的代码如下:

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;

};

即使T没有任何名为f的函数,也应该编译。不幸的是,两个版本都因错误而失败。

实施结果

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

template <class... Args>
class Param
{

    public:

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

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

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

};

3 个答案:

答案 0 :(得分:3)

我怀疑

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

应触发SFINAE并丢弃任何返回类型格式错误的f实例化(例如模糊或不存在的重载)而不是硬错误。我不太确定,因为Tproxy的参数,而不是f,我根本无法解析标准的相关部分(我相信大约14.8.2) 。非规范性说明中提供的所有例子似乎都不适用。

如果失败,可以使用

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

我的编译器很乐意接受proxy<int> p;,与第一个选项不同。 p.f();会导致“找不到匹配的函数”错误,就像SFINAE一样。


我建议尽可能使用独立运营商的形式:

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

是可能的。

答案 1 :(得分:1)

乍一看,这似乎微不足道:

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

C ++中的任何其他东西都不会给任何T的所有T成员Proxy<T>。唯一缺少的是ctors,但是从你的问题我推断你已经知道如何转发那些。

背景:实际上,T可能的成员名称集是无限的。因此,您无法在.f()中通过名称查找找到Proxy<T>,并且查找成员名称的唯一其他范围是基类范围。

答案 2 :(得分:1)

您需要将f的模板参数中proxy的存在性检查隔离一个额外级别。以下内容允许您以任何可以致电proxy<X>::f()的方式致电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)...);
    }
};

快速测试:

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

这适用于g ++ 4.5和g ++ 4.6 in -std = c ++ 0x mode。