繁体   English   中英

为什么std :: copy_if签名不限制谓词类型

[英]Why does the std::copy_if signature not constrain the predicate type

想象一下,我们有以下情况:

struct A
{
    int i;
};

struct B
{
    A a;
    int other_things;
};

bool predicate( const A& a)
{
    return a.i > 123;
}

bool predicate( const B& b)
{
    return predicate(b.a);
}

int main()
{
    std::vector< A > a_source;
    std::vector< B > b_source;

    std::vector< A > a_target;
    std::vector< B > b_target;

    std::copy_if(a_source.begin(), a_source.end(), std::back_inserter( a_target ), predicate);
    std::copy_if(b_source.begin(), b_source.end(), std::back_inserter( b_target ), predicate);

    return 0;
}

std::copy_if的调用都会产生编译错误,因为编译器无法正确重载predicate()函数的正确重载,因为std::copy_if模板签名接受任何类型的谓词:

template<typename _IIter, 
         typename _OIter, 
         typename _Predicate>
_OIter copy_if( // etc...

如果我将std::copy_if调用包装到更受约束的模板函数中,我发现重载std::copy_if有效:

template<typename _IIter, 
         typename _OIter, 
         typename _Predicate = bool( const typename std::iterator_traits<_IIter>::value_type& ) >
void copy_if( _IIter source_begin, 
              _IIter source_end, 
              _OIter target,  
              _Predicate pred)
{
    std::copy_if( source_begin, source_end, target, pred );
} 

我的问题是:为什么在STL中它不像这样受到限制? 从我所看到的情况来看,如果_Predicate类型不是返回bool并接受迭代输入类型的函数,那么无论如何它都会产生编译器错误。 那么为什么不将这个约束放在签名中,以便重载解析可以工作呢?

因为谓词不一定是函数,但它也可以是函子。 并且限制函子类型几乎是不可能的,因为只要它定义了operator()它就可以是任何东西。

实际上我建议你在这里将重载函数转换为多态函数:

struct predicate {
    bool operator()( const A& a) const
    {
        return a.i > 123;
    }

    bool operator()( const B& b) const
    {
        return operator()(b.a);
    }
}

并使用实例调用仿函数,即

std::copy_if(a_source.begin(), a_source.end(), std::back_inserter( a_target ), predicate());
std::copy_if(b_source.begin(), b_source.end(), std::back_inserter( b_target ), predicate());
//                                                                                      ^^ here, see the ()

然后在算法内选择正确的重载。

此问题不仅影响算法的谓词。 它发生在模板类型推导推导出重载函数的任何地方。 模板类型推导在重载解析之前发生,因此编译器缺少用于解决模糊性的上下文信息。

正确编写的约束可能非常复杂,因为它需要考虑参数和返回类型转换,绑定,lambda,functor, mem_fn等等。

解决歧义(IMHO)的一种简单方法是通过lambda调用谓词。

std::copy_if(a_source.begin(), a_source.end(), 
         std::back_inserter( a_target ), 
         [](auto&& x){ return predicate(std::forward<decltype(x)>(x)); });

这推迟了重载分辨率,直到模板类型推断之后。

如果我拒绝(或我的老板拒绝)升级到c ++ 14怎么办?

然后手动滚动相同的lambda:

struct predicate_caller
{
  template<class T>
  decltype(auto) operator()(T&& t) const 
  {
    return predicate(std::forward<T>(t));
  }
};

并且这样打电话:

std::copy_if(b_source.begin(), b_source.end(), 
             std::back_inserter( b_target ), 
             predicate_caller());

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM