【问题标题】:Reversing strings in a vector using for_each and bind使用 for_each 和 bind 反转向量中的字符串
【发布时间】:2010-12-02 14:17:09
【问题描述】:

我在想如何在一个“简单”行中使用单个for_each 命令来反转vector 中包含的strings。

是的,我知道使用自定义函子很容易,但我不能接受,不能使用 bind 来完成(至少我做不到)。

#include <vector>
#include <string>
#include <algorithm>

std::vector<std::string> v; 
v.push_back("abc");
v.push_back("12345");

std::for_each(v.begin(), v.end(), /*call std::reverse for each element*/);

编辑: 非常感谢那些有趣的解决方案。 但是,我的解决方案是不使用 Visual Studio 2008 功能包/SP1 附带的tr1::bind。我不知道为什么它不像预期的那样工作,但事实就是这样(even MS admits that it's buggy)。也许一些hotfixes 会有所帮助。

使用 boost::bind 一切都按预期工作,而且非常简单(但有时会很混乱:))。我真的应该首先尝试 boost::bind...

【问题讨论】:

  • “是的,我知道用扳手很容易做,但我不能接受不能用我的锤子做。”
  • @R.佩特:我发现知道如何在许多情况下使用我工具箱中的所有工具,即使其他工具会更好,这对于提高专业知识非常重要。我的宠物代码充满了这些东西。在生产代码中,我通常会尝试选择最好的(我知道,因为我已经对它们都进行了试验)。
  • 我还想强调一下,使用 bind / lambdas 更清晰:所做工作的定义是本地的,这有助于人们阅读它......如果它不是太混乱的话。
  • 这个问题确实有一个“算法”标签,因为它涵盖了 STL 算法的处理。 (再次添加标签)
  • 添加了 stl 算法 :)

标签: c++ stl vector tr1 stl-algorithm


【解决方案1】:

std::for_each 需要一个一元函数(或至少具有一元函数的 typedef 的东西)。

std::reverse 是一个二元函数。它需要两个迭代器。可以使用 boost::bind 将它们绑定在一起,但这将是一个非常可怕的混乱。比如:

boost::bind(
    &std::reverse<std::string::iterator>,
        boost::bind(&std::string::begin, _1), 
        boost::bind(&std::string::end, _1))

我认为更好的是编写一个名为 reverse_range 的可重用函数,如下所示:

template <class Range>
void reverse_range(Range& range)
{
    std::reverse(range.begin(), range.end());
}

(可能通过一些元编程来确保 Range& 不是双重引用)

然后在你的 for_each 中使用它(当然是在将其调整为一元函数之后)。

std::for_each(v.begin(), v.end(),
    std::ptr_fun(&reverse_range<std::string>));

编辑:

因为 string::begin 和 string::end 都有 const 和 non-const 变体,所以有必要强制转换它们(正如 litb 在我写它们以测试我的答案时发现的那样...... +1!)。这使它非常冗长。 Typedefs 可以让它更卫生一点,但要坚持单线主题:

boost::bind(
    &std::reverse<std::string::iterator>,
    boost::bind(
        (std::string::iterator (std::string::*)())&std::string::begin, _1),
    boost::bind(
        (std::string::iterator (std::string::*)())&std::string::end, _1)
    )
);

这只是为了重构而尖叫。

最后因为无聊,给C++0x加分:

std::for_each(v.begin(), v.end() [](std::string& s){ std::reverse(s); });

编辑:boost::bind 工作得很好,不需要 boost::lambda。

【讨论】:

  • 不错!这正是我想知道的。谢谢!
  • 我试过了,这个有效:boost::bind(&amp;std::reverse&lt;std::string::iterator&gt;, boost::bind((std::string::iterator(std::string::*)())&amp;std::string::begin, _1),boost::bind((std::string::iterator(std::string::*)())&amp;std::string::end, _1))
  • 对于这些情况,我的工具箱中有get_nonconst / get_const 函数:codepad.org/WI3w5xNh
【解决方案2】:

您必须滚动自己的反向对象:

struct Reverser
{
    void operator()(std::string& value) const
    {
        std::reverse(value.begin(),value.end());
    }
};

现在你可以一行完成:

std::for_each(v.begin(), v.end(), Reverser());

【讨论】:

    【解决方案3】:

    Boost.Phoenix2:

    std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1));
    

    用 mr-edd 来解释:可测量的更棒:)

    完整示例:

    #include <boost/spirit/home/phoenix.hpp>
    #include <algorithm>
    #include <vector>
    #include <string>
    #include <iostream>
    #include <iterator>
    
    int main(void)
    {
    
        using namespace boost::phoenix::arg_names; // for "arg1"
    
        std::vector<std::string> v;
        v.push_back("hello");
        v.push_back("world");
        std::for_each(v.begin(), v.end(), boost::phoenix::reverse(arg1));
    
        std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    }
    

    打印:

    olleh
    dlrow
    

    【讨论】:

    • +1 它还让我想起了我自己的所有 STL 算法的个人包装器,因此它们可以在完整容器上调用: range::for_each(v, range::reverse<:string>) ; - 简单:)
    • +1 不错。虽然我很喜欢 Boost.Spirit(只是整个事情的纯粹聪明让我不寒而栗,别介意它编译——最终——编译成如此小而快的东西),我没有给 Phoenix 太多时间。也许我应该去磨练那个工具。
    【解决方案4】:

    也可以使用 BOOST_FOREACH 宏来完成:

    BOOST_FOREACH( std::string& s, v )
        std::reverse( s.begin(), s.end() );
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-02
      • 1970-01-01
      • 1970-01-01
      • 2023-03-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多