вернуть массив std :: - PullRequest
       11

вернуть массив std ::

2 голосов
/ 02 марта 2012

Я реализую некоторые классы для представления отображений (в математическом смысле), т. Е. F: R x R ^ n -> R ^ n. Я хотел бы реализовать абстрактный базовый класс, который либо: 1) Принимает std :: array в качестве ссылки и модифицирует его или же 2) возвращает std :: array (по значению или по ссылке, не уверен?)

Синтаксис варианта 2 более желателен для меня просто потому, что я думаю, что код будет больше похож на математику, которую я пытаюсь представить, но я хочу убедиться, что я не копирую ненужные вещи и не вызываю кучу нежелательных накладные расходы. Например, если бы я имел:

// Function f declaration
std::array<double, 4> f(double t, const std::array<double, 4> & x);

// Some code snippet that uses f
std::array<double, 4> x = {0.0, 1.0, 2.0, 3.0};
double t = 0.0;
std::array<double, 4> dxdt = f(t, x);

Как я могу определить, выполняется ли копирование в последней строке, или как я могу убедиться, что не произойдет ?

В определении f (), что мне нужно сделать (если что-нибудь), чтобы гарантировать, что это будет возвращено без вызова конструктора копирования? Я хочу, чтобы использование было простым, чтобы клиенты не использовали указатели или умные указатели, но, возможно, это необходимо?

Я знаю, что могу изменить это, чтобы вернуть void, и просто сделать один из аргументов std :: array dxdt, но мне больше нравится синтаксис возвращаемого значения, если нет проблем с производительностью или проблем утечки памяти.

Ответы [ 4 ]

8 голосов
/ 02 марта 2012

Это должно быть чем-то, что вас не должно волновать, поскольку оптимизация копий является проблемой компилятора. Но компиляторы все еще являются машинами на основе Тьюринга, которые сами не могут придумать оптимизацию.

Насколько я знаю, большинство современных компиляторов ведут себя, если функция:

  • иметь один оператор возврата или ...
  • имеют несколько возвратов, возвращающих одну и ту же переменную

Компилятор размещает эту переменную в стеке в том месте, где она должна быть оценена выражением, которому принадлежит вызов функции. Для этого не сгенерировано ни одной копии.

Если ваша функция имеет несколько выходов, возвращающих разные выражения, это, например, в некоторых случаях, сделать невозможно.

Итак, в общем, если вам нужно вернуть какой-то широкий объект, просто объявите его и убедитесь, что все операторы возврата возвращают именно его.

2 голосов
/ 02 марта 2012

Не волнуйтесь: это очень распространенный вариант использования, хорошо понятный компиляторам.Не забудьте включить оптимизацию, хотя.Еще одна вещь, если ваш массив длиной 4 double, у вас, вероятно, есть более важные вещи, которые нужно оптимизировать перед этим.Не забывайте, что оптимизация до того, как вы узнаете, что она вам нужна (то есть, вы измерили), является злом.

Теперь, если вы действительно хотите убедиться, что нет копии, вы можете взглянуть на код сборки (как зависит от вашего компилятора / инструментов).

В качестве альтернативы вы можете написать собственный класс, где вы печатаете что-то в конструкторе копирования и оператор присваивания, чтобы убедиться, что они не вызываютсяно вы не сможете сделать это с std::array<>, если вы не извлечете из него недопустимое значение (хотя оно будет скомпилировано и запущено).

1 голос
/ 02 марта 2012

Полагаю, вам не нужно беспокоиться о ненужном копировании, так как компилятор довольно хорош в оптимизации этих вещей. Он может использовать RVO (оптимизацию возвращаемого значения) для устранения ненужных копий. И если у вас есть компилятор, поддерживаемый C ++ 11, копирование еще более сокращается из-за семантики move.

0 голосов
/ 02 марта 2012

Если вы вернетесь по ссылке, то операция копирования не будет выполняться (на самом деле мы имеем дело с 4 байтами, адресом памяти).

std::array<double, 4> f(double t, const std::array<double, 4> & x);

... можно превратить в:

std::array<double, 4>& f(double t, const std::array<double, 4> & x);

... при условии, что возвращаемым значением является , а не локальная переменная, выделенная в стеке, внутри самой функции (потому что тогда она будет зависать после возврата).

Компилятор, вероятно, будет делать это автоматически в некоторых случаях, но лучше сказать об этом лучше - как вы говорите, вы хотите обеспечить копирование не происходит.

...