【问题标题】:Introducing a dummy argument via STL or Boost通过 STL 或 Boost 引入虚拟参数
【发布时间】:2011-05-03 18:20:44
【问题描述】:

假设一个人想用随机数填充一个向量。那么有以下明显的解决方案:

vector<int> result;
result.resize(n);
for (int i = 0; i < n; ++i) {
    result[i] = generateRandomNumber();
}

好的,它显然有效,但我想了解摆脱 for 循环的最简单的 STL/Boost 方法是什么。使用 std::transform 很诱人,但它需要一个带有一个参数的函数。有没有什么好的 STL 方法可以在函数中引入虚拟参数?

【问题讨论】:

    标签: c++ boost stl


    【解决方案1】:

    C++标准库有std::generate()std::generate_n()

    例如:

    #include <iostream>
    #include <cstdlib>
    #include <algorithm>
    #include <vector>
    #include <iterator>
    int generateRandomNumber()
    {
        return std::rand();
    }
    int main()
    {
        int n = 10;
        std::vector<int> result;
        generate_n(back_inserter(result), n, generateRandomNumber);
        copy(result.begin(), result.end(), std::ostream_iterator<int>(std::cout, " "));
        std::cout << '\n';
    }
    

    测试:https://ideone.com/5xD6P

    至于第二个问题,如果我理解正确的话,是如何创建一个接受 int 参数、忽略它并调用您的int f() 的函子?

    C++98 的方式是真正写出整个仿函数:

    struct IgnoreArgument
    {
        typedef int(*fp_t)();
        fp_t fp;
        IgnoreArgument(fp_t f) : fp(f) {}
        int operator()(int) const { return fp(); }
    };
    ...
    transform(v.begin(), v.end(), v.begin(), IgnoreArgument(f));
    

    测试:https://ideone.com/DTsyl

    C++11 的方式是使用 lambda 表达式

    transform(v.begin(), v.end(), v.begin(), [](int){return f();});
    

    测试:https://ideone.com/nAPXI

    而C++98/boost的方式是使用boost::bind

    transform(v.begin(), v.end(), v.begin(), boost::bind(f));
    

    测试:https://ideone.com/cvd88

    【讨论】:

    • 非常感谢。但是关于虚拟变量的问题仍然悬而未决。
    • @Ilya Razenshteyn 你能举例说明如何使用这种变量吗?
    • 假设我有一个函数int f(),现在我想产生一个函数int g(int) { return f(); } 使用标准的东西。
    • 为什么不generate_n(back_inserter(result), n, std::rand);
    • @Rob OP 有 generateRandomNumber(),我只给了它示例内容以有一个可编译的示例。
    【解决方案2】:

    generate_n 与您想要的元素数量一起使用,并使用back_insert_iterator 指向您希望将它们存储到的向量,以及指向生成数字的函数的指针。

    #include <vector>
    #include <algorithm>
    
    
    int generateRandomNumber()
    {
        static int i = 0;
        return 42 + (i++);
    }
    
    int main()
    {
        std::vector<int> vi;
        std::generate_n(back_inserter(vi), 10, &generateRandomNumber);    
    }
    

    请注意,通过使用back_insert_iterator,就像我在这里所做的那样,您不必预先设置向量的大小,这充其量是很笨拙的。

    【讨论】:

    • @Ilya:然后将我示例中的back_inserter 替换为.begin()
    • 谢谢,但是,关于虚拟变量的问题仍然悬而未决。很高兴找到它。
    • @Ilya:也许我不明白这个问题?什么虚拟变量?
    【解决方案3】:

    这里的问题是transform 不是手头任务的正确选择。 transform 的目的是获取一些输入,以某种规定的方式转换每个输入,并为每个输入生成一个输出。

    在这种情况下,您没有任何输入。如果向量中的值(以某种方式或其他方式)基于某个现有向量中的值,transform 将是有意义的。

    generate_n 确实是该问题的正确解决方案——它旨在调用某些函数/函子 N 次,产生 N 个结果,并将这些结果分配给您提供的输出迭代器(及其后继者)。由于它旨在生成值(而不是转换现有值),因此函数/仿函数不需要输入,您也不必提供“假”输入。

    就“虚拟”论点而言,想要/需要它可能是一个很好的迹象,表明(在这种情况下)您使用了错误的算法,并且不应该这样做。

    但是,您确实有时会遇到相反的情况:您想使用一个提供参数的算法,但您希望能够提供一个参数。例如,假设您希望能够将数组中的数字设置为随机数,并由用户指定一些下限和/或上限。在这种情况下,您希望能够指定将传递给您的随机数函数的边界,但 generategenerate_n 都没有这样做的任何规定。

    在这种情况下,您有两种可能性。一个是 bind(最初是 boost::bind,但现在包含在 C++11 中)。我通常更喜欢使用仿函数,并将参数传递给 ctor:

    class gen_random { 
        int lower;
        int upper;
    public:
        gen_random(int lower = 0, int upper = RAND_MAX) : lower(lower), upper(upper) {}
    
        int operator() { return rand_range(lower, upper);
    };
    
    int main() { 
        std::vector<int> rand_ints;
    
        std::generate_n(std::back_inserter(rand_ints), 10, gen_random(1, 6));
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2013-06-17
      • 1970-01-01
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多