【问题标题】:C++ std::transform vector of pairs->first to new vectorC++ std::transform vector of pairs->first to new vector
【发布时间】:2012-02-24 00:48:01
【问题描述】:

抱歉,有点初学者问题。有向量和向量对

typedef std::vector <int> TItems;
typedef std::vector < std::pair <int, int> > TPairs;

有什么方法可以一步将所有第一项成对转换为另一个向量

int main ()
{
TItems items;
TPairs pairs;

pairs.push_back (std::make_pair(1,3));
pairs.push_back (std::make_pair(5,7));

std::transform( items.begin(), items.end(), items.begin(), comp ( &pairs ) );

return 0;
}

如何设计函子?

class comp
{
private:
     TPairs *pairs;

public:
    comp ( TPairs  *pairs_ ) : pairs ( pairs_) { }

    unsigned int operator () ( const unsigned int index ) const
    {
        return  (*pairs)[index].second != pairs->end();  //Bad idea
    }
};

也许有一些更用户友好的方法,没有 lambda 表达式和循环。感谢您的帮助。

【问题讨论】:

    标签: c++ vector transform functor std-pair


    【解决方案1】:

    首先,您应该使用back_inserter 作为transform 的第三个参数,以便将转换后的值推到向量的后面。

    其次,您需要某种函子,它接受一对整数并返回第一个整数。应该这样做:

    int firstElement( const std::pair<int, int> &p ) {
        return p.first;
    }
    

    现在,把各个部分放在一起:

    TPairs pairs;
    pairs.push_back( std::make_pair( 1, 3 ) );
    pairs.push_back( std::make_pair( 5, 7 ) );
    
    TItems items;
    std::transform( pairs.begin(), pairs.end(), std::back_inserter( items ),
                    firstElement );
    

    在此代码之后,items 包含 1 和 5。

    【讨论】:

    • 有什么聪明的方法可以使用std::get<0> 代替你的自定义函数吗?
    【解决方案2】:

    请参阅 frerich 或 kotlinski 对 C++03 的回答。

    使用 lambda 的 C++11 解决方案:

    std::transform(pairs.begin(), 
                   pairs.end(), 
                   std::back_inserter(items), 
                   [](const std::pair<int, int>& p) { return p.first; });
    

    【讨论】:

    • 糟糕,我没有注意到“没有 lambda”的要求,但是为什么它很简单并且是语言的一部分呢?
    • 我相信它不是 C++ 语言的一部分,这里的大多数人都可以实际使用(由于编译器的限制或工作场所的某些要求)。
    【解决方案3】:

    我真的希望你使用std::get 作为仿函数,因为它已经作为库函数提供了!!

    如果我们能写出这行不是很棒吗!?

    std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
    

    ...但它比这更可怕。您需要明确使用哪个get

    int main() {
      std::vector<int> items;
      std::vector<std::pair<int, int>> pairs;
    
      pairs.push_back(std::make_pair(1, 3));
      pairs.push_back(std::make_pair(5, 7));
    
      std::transform(pairs.begin(), pairs.end(), std::back_inserter(items),
                     (const int& (*)(const std::pair<int, int>&))std::get<0>);
    
      return 0;
    }
    

    问题是,std::getis overloaded 将 1.pair&amp;、2.const pair&amp; 和 3.pair&amp;&amp; 作为参数,因此它适用于任何类型的对作为输入。不幸的是,重载妨碍了std::transform 的模板类型推导,所以我们原来的那行

    std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
    

    产量

     error: no matching function for call to ‘transform(std::vector<std::pair<int, int> >::iterator, std::vector<std::pair<int, int> >::iterator, std::back_insert_iterator<std::vector<int> >, <unresolved overloaded function type>)’
       std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
                                                                                        ^
    ...
    
    /usr/include/c++/4.8/bits/stl_algo.h:4915:5: note:   template argument deduction/substitution failed:
     note:   couldn't deduce template parameter ‘_UnaryOperation’
       std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
    

    在推导std::transform的模板时,它不知道你要求std::get的哪个重载,所以你必须手动指定它。将函数指针转换为正确的类型会告诉编译器,“嘿,请使用重载,get 接受 const&amp; 并返回 const&amp;!”

    但至少我们使用的是标准库组件(耶)?

    就行数而言,它并不比其他选项差: http://ideone.com/6dfzxz

    【讨论】:

    • 谁能想到任何改进?能够像这样干净地使用std::get 真是太好了。 ...真的我应该使用reinterperet_cast&lt;const int&amp; (*)(const std::pair&lt;int, int&gt;&amp;)&gt;(std::get&lt;0&gt;),但这似乎更糟...
    • 我认为可以用包装在 lambda 中的 get 函数替换“硬”强制转换,可以为其指定参数
    【解决方案4】:

    这个怎么样?

    items.reserve(pairs.size());
    for (size_t it = 0; it < pairs.size(); ++it) {
        items.push_back(pairs[it].first);
    }
    

    易于理解和调试。

    【讨论】:

    • @kotlinski:谢谢,但这是一个常见的解决方案。如果可能的话,我想找到没有任何循环的一步解决方案。
    • 你要求一些用户友好的东西,然后用一些 C++ 暴行发布答案会误导:)
    • +1:在这种情况下最简单。如果它们简化了,为什么要避免循环?
    • @stefaanv:因为 OP 明确要求回答 without lambda expressions and loops.
    • @Frerich:我的原因实际上是指那个限制,但我承认这并不清楚。
    【解决方案5】:

    std::bind怎么样?

    std::transform(pairs.begin(), 
                   pairs.end(), 
                   std::back_inserter(items), 
                   std::bind(&TPairs::value_type::first, std::placeholders::_1));
    

    (对于非 C++11 代码,将 std::bind 替换为 boost::bind

    【讨论】:

      【解决方案6】:

      C++11 的另一种可能性是std::mem_fn,这类似于std::bind 的解决方案:

      std::transform(pairs.begin(), 
                     pairs.end(), 
                     std::back_inserter(items), 
                     std::mem_fn(&std::pair<int,int>::first)               
      );
      

      【讨论】:

        猜你喜欢
        • 2016-02-04
        • 2022-12-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-07
        • 2011-01-01
        • 2020-01-26
        • 2020-09-22
        相关资源
        最近更新 更多