为什么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
的调用都会产生编译错误,因为std::copy_if
模板签名接受任何类型的谓词,因此编译器无法推断predicate()
函数的正确重载:
template<typename _IIter,
typename _OIter,
typename _Predicate>
_OIter copy_if( // etc...
我发现如果我将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、函子、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));
}
};
和call如下:
std::copy_if(b_source.begin(), b_source.end(),
std::back_inserter( b_target ),
predicate_caller());
相关文章:
- std::condition_variable::wait()如何评估给定的谓词
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- 函数作为模板参数,是否对返回类型强制约束
- 约束和显式模板实例化
- IpOpt拒绝解决不受约束的问题
- 使用C++模板时表达约束
- 如何在 SCIP C++ 接口中获取 MILP 约束矩阵中的系数值
- 受约束的成员函数和显式模板实例化
- 如何在 STL 函数中找到传递给谓词的元素的索引?
- 具有多种约束(例如重量、体积等)的背包
- Gecode 与 Z3 用于约束随机化
- 为 C++11 算法组合多个谓词
- 如果原型是本地的,则使用流 I/O C++类型约束将失败
- 处理 std::enable_if<...中谓词的逻辑"OR">
- C++打开具有 2 个约束的文件
- std::map与谓词与初始化列表
- 尝试使用谓词函数会导致错误:"std::sort"未找到匹配的重载函数
- 如何拥有受约束的运算符模板?
- 如何有效地操作满足给定谓词的向量中的所有项目?
- 为什么std::copy_if签名不约束谓词类型