返回一堆值的接口

Mar*_*tin 27 c++ c++11 c++98

我有一个函数,它接受一个数字并返回那么多东西(比方说,整数).什么是最干净的界面?一些想法:

  1. 回来一个vector<int>.矢量将被复制多次,这是低效的.
  2. 回来一个vector<int>*.我的getter现在必须分配向量本身以及元素.有谁必须释放向量的所有常见问题,你不能分配一次并使用相同的存储器来进行对getter的许多不同调用等.这就是为什么STL算法通常避免分配内存,而是想要它传入了.
  3. 回来一个unique_ptr<vector<int>>.现在很清楚是谁删除了它,但我们还有其他问题.
  4. 以a vector<int>作为参考参数.getter可以push_back()和调用者决定是否到reserve()空间.但是,如果传入vector非空,那么getter应该怎么做?附加?通过先清除它来覆盖?断言它是空的?如果函数的签名只允许一个解释,那将是很好的.
  5. 传递一个beginend迭代器.现在我们需要返回实际写入的项目数(可能小于预期),并且调用者需要注意不要访问从未写入的项目.
  6. 让吸气者接受iterator,呼叫者可以通过insert_iterator.
  7. 放弃,只是通过一个char *.:)

And*_*owl 37

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

它使你的函数的签名清晰,传达你只想要返回一个整数向量,并且它将是有效的,因为不会发出任何副本:std::vector将调用移动构造函数(或者,很可能,命名返回将应用值优化,导致无移动且无副本):

std::vector<int> foo()
{
    std::vector<int> v;
    // Fill in v...
    return v;
}
Run Code Online (Sandbox Code Playgroud)

这样您就不必处理诸如所有权,不必要的动态分配以及其他只是污染问题简单性的问题:返回一堆整数.

在C++ 03中,您可能希望使用选项4并对非const向量进行左值引用:C++ 03中的标准容器不支持移动,并且复制向量可能很昂贵.从而:

void foo(std::vector<int>& v)
{
    // Fill in v...
}
Run Code Online (Sandbox Code Playgroud)

但是,即使在这种情况下,您也应该考虑这种惩罚对您的用例是否真的重要.如果不是,您可能会选择更清晰的功能签名,但会牺牲一些CPU周期.

此外,C++ 03编译器能够执行命名返回值优化,因此即使理论上临时应该从您返回的值进行复制构造,实际上也不会发生复制.

  • @ MartinC.Martin :(很抱歉这么晚才回到这里,我已经被其他讨论所吸引) - 然而,首先是偏爱简约和优雅.如果性能成为一个问题,请考虑优化和重新设计您的程序,以便它执行较少的分配.但如果这不是问题,请选择最简单的设计. (3认同)
  • @AndyProwl:忘记5,选项6更好. (3认同)

Use*_*ess 11

你自己写的:

...这就是为什么STL算法通常会避免分配内存,而是希望它传入

除了STL算法一般"想记忆通过",他们对迭代器操作来代替.这特别是为了将算法与容器分离,从而产生:

选项8

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

最简单的方法是使用boost::function_input_iterator,但下面是草图机制(主要是因为我输入的速度比思考速度快).


输入迭代器类型

(使用C++ 11,但您可以用std::function函数指针替换或只是硬编码生成逻辑):

#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);
    }
};
Run Code Online (Sandbox Code Playgroud)

示例生成器功能

(同样,用C++ 98的外联函数替换lambda是微不足道的,但是这样输入更少)

#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>();
}
Run Code Online (Sandbox Code Playgroud)

使用示例

#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, ", "));
}
Run Code Online (Sandbox Code Playgroud)