【问题标题】:Checking whether a non-hardwired member function exists with SFINAE使用 SFINAE 检查是否存在非硬连线成员函数
【发布时间】:2011-09-02 05:04:12
【问题描述】:

我想为成员函数和运算符创建代理。它们必须具有相同的返回类型和参数,并且必须适用于多个类,这些类作为模板参数给出。即使该类没有特定的成员函数或运算符,我希望它编译而不是因错误而失败,本质上是 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 的函数,它也应该编译。不幸的是,两个版本都因错误而失败。

resultofbeing的实现

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

};

【问题讨论】:

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


    【解决方案1】:

    我怀疑

    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&lt;int&gt; 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;
    }
    

    有可能。

    【讨论】:

    • 是的,这个解决方案似乎运行良好,但对操作员来说很奇怪;需要decltype(std::declval&lt;T&amp;&gt;()(std::declval&lt;Args&gt;()...)) 而不是decltype(std::declval&lt;T&amp;&gt;().operator () (std::declval&lt;Args&gt;()...))
    • @Frigo 也许这值得提出自己的问题,我看不出你在做什么有什么特别之处。如果您使用#define DEFINE_FORWARDING_MEMBER(member_name) \ 并将上面的内容粘贴为多行宏,将f 替换为member_name,会发生什么?
    • @Frigo 使用lval(arg0, ...) 应该优先于lval.operator()(arg0, ...),因为合法的函子可能会通过例如暴露其功能。转换运算符,而不是 operator()。应根据有效表达式指定通用代码,而不是参数类型是否具有operator()——这些是实现细节。
    • @Frigo 添加了operator+ 的示例。恐怕你不能用一个宏来做这一切——充其量你可以为每个 arity 设置一个宏。对于那些必须是成员的运算符,每个参数都有一个单独的宏(或手动执行)。
    • @Frigo SFINAE 仅适用于模板,并且成员函数不是模板,即使它们是模板的成员。一元 operator- 的类内声明可能类似于 template&lt;typename U = T&gt; decltype( -std::declval&lt;U&gt;() ) operator-() const;
    【解决方案2】:

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

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

    C++ 中没有其他任何东西会给Proxy&lt;T&gt; T 的所有成员,对于任何 T。唯一缺少的是 ctors,但从你的问题我推断你已经知道如何转发这些。

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

    【讨论】:

    • 从技术上讲,Proxy 有一个指向 T 而不是成员变量的指针,并进行额外检查以确保它对于某些操作不为 NULL,如果是则抛出异常。我只是不想让问题进一步复杂化。尽管如此,还是感谢您的意见,我真的很感激。
    • C++0x 带来了继承的构造函数:using T::T; 让您的解决方案完整。
    【解决方案3】:

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

    这适用于 -std=c++0x 模式下的 g++ 4.5 和 g++ 4.6。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-23
      • 1970-01-01
      • 1970-01-01
      • 2016-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-08
      相关资源
      最近更新 更多