[英]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.