簡體   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