C++ 用于返回一组值的接口

C++ 用于返回一组值的接口,c++,c++11,c++98,C++,C++11,C++98,我有一个函数,它接受一个数字并返回很多东西(比如int)。最干净的界面是什么?一些想法: 返回一个向量。向量将被复制多次,这是低效的 返回一个向量*。我的getter现在必须分配向量本身以及元素。所有常见的问题都是谁必须释放向量,不能分配一次并使用同一个存储来多次调用getter,等等。这就是为什么STL算法通常避免分配内存,而希望传递内存 返回一个唯一的\u ptr。现在很清楚是谁删除了它,但我们还有其他问题 将向量作为参考参数。getter可以push_back(),调用者可以决定是否res

我有一个函数,它接受一个数字并返回很多东西(比如int)。最干净的界面是什么?一些想法:

  • 返回一个
    向量
    。向量将被复制多次,这是低效的
  • 返回一个
    向量*
    。我的getter现在必须分配向量本身以及元素。所有常见的问题都是谁必须释放向量,不能分配一次并使用同一个存储来多次调用getter,等等。这就是为什么STL算法通常避免分配内存,而希望传递内存
  • 返回一个
    唯一的\u ptr
    。现在很清楚是谁删除了它,但我们还有其他问题
  • 向量
    作为参考参数。getter可以
    push_back()
    ,调用者可以决定是否
    reserve()
    空间。但是,如果传入的
    向量
    为非空,getter应该怎么做?追加是否先清除它来覆盖?断言它是空的?如果函数的签名只允许一个解释就好了
  • 传递
    开始
    结束
    迭代器。现在,我们需要返回实际写入的项目数(可能比期望的要少),调用方需要小心不要访问从未写入的项目
  • 让getter使用
    迭代器
    ,调用者可以传递
    插入迭代器
  • 放弃,只需传递一个
    char*
    :)
    return
    vector
    ,它不会被复制,而是会被移动。

    在C++11中,标准容器支持移动语义,您应该使用选项1

    它使函数的签名清晰,表明您只希望返回一个整数向量,这将是有效的,因为不会发出副本:将调用
    std::vector
    的move构造函数(或者,很可能会应用命名返回值优化,导致不移动和不复制):

    但是,即使在这种情况下,你也应该考虑这个惩罚对你的用例是否真的有意义。如果不是,您可能会选择更清晰的函数签名,但会牺牲一些CPU周期


    此外,C++03编译器能够执行命名返回值优化,因此,尽管理论上应该根据返回的值构造临时副本,但实际上不可能发生复制。

    在C++11中,正确的答案是返回
    std::vector
    就是返回它,确保它将被显式或隐式移动。(更喜欢隐式移动,因为显式移动会阻止某些优化)

    有趣的是,如果您担心重用缓冲区,最简单的方法是插入一个可选参数,该参数按如下值接受
    std::vector

    std::vector<int> get_stuff( int how_many, std::vector<int> retval = std::vector<int>() ) {
      // blah blah
      return retval;
    }
    
    如果必须为每个隐藏
    的实现,则可能使用
    std::function

    困难的方法是返回一个生成器或一对迭代器来生成所讨论的元素

    当您一次只想处理一个元素,并且如果生成有问题的值很昂贵(可能需要遍历内存),这两种方法都可以避免无意义地分配缓冲区

    在C++98中,我将使用一个
    向量&
    clear()
    它。

    你自己写的:

    …这就是为什么STL算法通常避免分配内存,而希望传递内存的原因

    除了STL算法通常不“希望传递内存”,它们在迭代器上运行。这是为了将算法与容器解耦,从而产生:

    备选案文8 通过返回输入迭代器,将值生成与这些值的使用和存储分离

    最简单的方法是使用,但下面是一个草图机制(主要是因为我打字比思考快)


    输入迭代器类型 (使用C++11,但可以用函数指针替换
    std::function
    ,也可以硬编码生成逻辑):

    #包括
    #包括
    模板
    类生成器:公共std::迭代器{
    整数计数;
    std::函数生成;
    公众:
    生成器():计数(0){}
    生成器(int count,std::function func):计数(count)
    ,生成{(func){}
    发电机(发电机常数和其他):计数(其他.计数)
    ,生成{(其他.生成{}
    //为简洁起见,省略了移动、赋值等
    T运算符*(){return generate_u();}
    生成器和运算符++(){
    --伯爵;
    归还*这个;
    }
    生成器运算符++(int){
    发电机tmp(*此);
    ++*这,;
    返回tmp;
    }
    布尔运算符==(生成器常量和其他)常量{
    返回计数=other.count;
    }
    布尔运算符!=(发电机常数和其他)常数{
    返回!(*此==其他);
    }
    };
    
    示例生成器函数 (同样,对于C++98来说,用一个离线函数替换lambda是很简单的,但这是较少的类型)

    #包括
    生成器开始随机整数(int n){
    静态标准:minstd_rand prng;
    静态标准::统一分布pdf;
    发电机rv(n,
    [](){返回pdf(prng);}
    );
    返回rv;
    }
    生成器结束\u随机\u整数(){
    返回生成器();
    }
    
    示例使用
    #包括
    #包括
    #包括
    int main()
    {
    使用名称空间std;
    矢量输出;
    
    你能用C++11编译器吗?是的,我用的是C++11,但我也很好奇如何在C++98中实现。在C++11中,向量是最好的,因为它不会被复制多次。在这两种情况下,向量的设计仍然很差,除非有充分的理由,否则它不能被复制
    void foo(std::vector<int>& v)
    {
        // Fill in v...
    }
    
    std::vector<int> get_stuff( int how_many, std::vector<int> retval = std::vector<int>() ) {
      // blah blah
      return retval;
    }
    
    #include <iostream>
    struct Foo {
      int get_element(int i) const { return i*2+1; }
      template<typename Lambda>
      void for_each_element( int up_to, Lambda&& f ) {
        for (int i = 0; i < up_to; ++i ) {
          f( get_element(i) );
        }
      }
    };
    int main() {
      Foo foo;
      foo.for_each_element( 7, [&](int e){
        std::cout << e << "\n";
      });
    }
    
    #include <functional>
    #include <iterator>
    template <typename T>
    class Generator: public std::iterator<std::input_iterator_tag, T> {
        int count_;
        std::function<T()> generate_;
    public:
        Generator() : count_(0) {}
        Generator(int count, std::function<T()> func) : count_(count)
                                                      , generate_(func) {}
        Generator(Generator const &other) : count_(other.count_)
                                          , generate_(other.generate_) {}
        // move, assignment etc. etc. omitted for brevity
        T operator*() { return generate_(); }
        Generator<T>& operator++() {
            --count_;
            return *this;
        }
        Generator<T> operator++(int) {
            Generator<T> tmp(*this);
            ++*this;
            return tmp;
        }
        bool operator==(Generator<T> const &other) const {
            return count_ == other.count_;
        }
        bool operator!=(Generator<T> const &other) const {
            return !(*this == other);
        }
    };
    
    #include <random>
    Generator<int> begin_random_integers(int n) {
        static std::minstd_rand prng;
        static std::uniform_int_distribution<int> pdf;
        Generator<int> rv(n,
                          []() { return pdf(prng); }
                         );
        return rv;
    }
    Generator<int> end_random_integers() {
        return Generator<int>();
    }
    
    #include <vector>
    #include <algorithm>
    #include <iostream>
    int main()
    {
        using namespace std;
        vector<int> out;
    
        cout << "copy 5 random ints into a vector\n";
        copy(begin_random_integers(5), end_random_integers(),
             back_inserter(out));
        copy(out.begin(), out.end(),
             ostream_iterator<int>(cout, ", "));
    
        cout << "\n" "print 2 random ints straight from generator\n";
        copy(begin_random_integers(2), end_random_integers(),
             ostream_iterator<int>(cout, ", "));
    
        cout << "\n" "reuse vector storage for 3 new ints\n";
        out.clear();
        copy(begin_random_integers(3), end_random_integers(),
             back_inserter(out));
        copy(out.begin(), out.end(),
             ostream_iterator<int>(cout, ", "));
    }