Предотвратите ненужные копии объектов функтора C++

У меня есть класс, который накапливает информацию о ряде объектов и может действовать или как функтор или как выходной итератор. Это позволяет мне делать вещи как:

std::vector v;
Foo const x = std::for_each(v.begin(), v.end(), Joiner());

и

Foo const x = std::copy(v.begin(), v.end(), Joiner());

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

Если я создаю функтор как lvalue, компилятор создает две дополнительных копии вместо одной:

Joiner joiner;
Foo const x = std::copy(v.begin(), v.end(), joiner);

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

x = std::copy&>(...));

Я могу сделать копии дешевыми при помощи ссылки на состояние, а не само состояние в функторе в стиле std::inserter, продвижение к чему-то вроде этого:

Foo output;
std::copy(v.begin(), v.end(), Joiner(output));

Но это лишает возможности использовать "функциональный" стиль неизменных объектов и просто обычно не так хорошо.

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

16
задан Tim Sylvester 8 February 2010 в 00:00
поделиться

4 ответа

Изменить: основываясь на предложении Мэтта, и решить проблему gnibbler re positional-argument подход; можно проверить, указан ли дополнительный аргумент, специфичный для подкласса, аналогично ответу Алекса :

class B(A):
  def __init__(self, *args, **kwargs):
    try:
      self._w = kwargs.pop('w')
    except KeyError:
      pass
    super(B,self).__init__(*args, **kwargs)

>>> b = B(1,2,w=3)
>>> b.a
1
>>> b.b
2
>>> b._w
3

Исходный ответ:
Та же идея, что и ответ Мэтта , используя вместо этого super () .

Используйте метод super () для вызова метода суперкласса __ init __ () , а затем продолжайте инициализацию подкласса:

class A(object):
  def __init__(self, a, b):
    self.a = a
    self.b = b

class B(A):
  def __init__(self, w, *args):
    super(B,self).__init__(*args)
    self.w = w
-121--2209086-

Я всегда использую класс помощника, куда я могу поместить все мои не Я имею в виду, что это движение помощников все еще является OO и имеют методы вместо функций, с преимуществом, что вы можете организовать свои функции в различных помощников. Как StringHelper, DBHelper и т.д.

-121--3320661-

Вы часто жаловались на поведение с помощью < алгоритм > . Нет никаких ограничений на то, что они могут сделать с функтором, поэтому ответ на ваш вопрос - нет: нет пути побудить компилятор к элизированию копий. Это не (всегда) компилятор, это реализация библиотеки . Они просто любят передавать функторы по значению (подумайте о std:: sort doing a qsort, передавая функтор по значению рекурсивным вызовам и т.д.).

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

Я нашел это ироничным:

Но это делает невозможным использование «функционального» стиля неизменяемых объектов, и просто в целом не так приятно.

... поскольку весь этот вопрос основан на наличии сложного функтора, способного определять состояние, где создание копий проблематично. Если бы вы использовали «функциональный» стиль неизменяемых объектов, это было бы не проблема - дополнительные копии не были бы проблемой, не так ли?

15
ответ дан 30 November 2019 в 21:53
поделиться

Требуется перенаправление 301, 302 - временное, 301 - постоянное . В этом примере context является

context.Response.Status = "301 Moved Permanently";
context.Response.StatusCode = 301;
context.Response.AppendHeader("Location", nawPathPathGoesHere);
-121--3665272-

. Лично я склонен предпочитать то, что выглядит наилучшим образом, поскольку читаемость кода очень важна, особенно в рабочей среде. С точки зрения наилучшей практики, я боюсь, что я не уверен, однако это, как правило, лучшая практика, чтобы оптимизировать последний смысл, что вы должны написать его для удобочитаемости, а затем, если вы сталкиваетесь со скоростными проблемами сделать некоторые рефакторинг.

Любые проблемы, которые у вас есть с эффективностью, вероятно, будут в другом месте в вашем коде, если вы не делаете миллионы эхо.

Еще одно соображение - использование MVC для отделения ваших «представлений» от всей вашей бизнес-логики, что является очень чистым способом кодирования. Использование шаблонной структуры, такой как умный, может сделать этот шаг дальше, приводя к эпической победе.

-121--1734826-

Если у вас есть недавний компилятор (по крайней мере Visual Studio 2008 SP1 или GCC 4.4, я думаю), вы можете использовать std:: ref/std:: cref

#include <string>
#include <vector>
#include <functional> // for std::cref
#include <algorithm>
#include <iostream>

template <typename T>
class SuperHeavyFunctor 
{
    std::vector<char> v500mo;
    //ban copy
    SuperHeavyFunctor(const SuperHeavyFunctor&);
    SuperHeavyFunctor& operator=(const SuperHeavyFunctor&);
public:
    SuperHeavyFunctor():v500mo(500*1024*1024){}
    void operator()(const T& t) const { std::cout << t << std::endl; }
};

int main()
{
    std::vector<std::string> v; v.push_back("Hello"); v.push_back("world");
    std::for_each(v.begin(), v.end(), std::cref(SuperHeavyFunctor<std::string>()));
    return 0;
}

Edit: На самом деле, реализация MSVC10 reference_wrapper, кажется, не знает, как вывести возвращаемый тип функции Мне пришлось вывести SuperTaxedFunctor из std:: unary _ function < T, void > , чтобы он работал.

4
ответ дан 30 November 2019 в 21:53
поделиться

RVO - это именно это - оптимизация возвращаемых значений. В большинстве современных компиляторов она включена по умолчанию. Однако передача аргументов - это не возвращение значения. Возможно, вы не можете ожидать, что одна оптимизация подойдет везде.

Условия для устранения копирования четко определены в 12.8, параграф 15, пункт 3.

Когда временный объект класса, который не был привязан к ссылке (12.2) будет скопирован в объект класса с того же самого cv-незаконного типа, операция копирования операция копирования может быть пропущена путем конструирования временного объекта непосредственно в объект опущенной копии

[выделение мое]

LHS Foo является const квалифицированным, а временный - нет. ИМХО, это исключает возможность copy-elision.

1
ответ дан 30 November 2019 в 21:53
поделиться

Небольшое примечание: for_each , накопление , преобразование (2-я форма) , не обеспечивают гарантии порядка при прохождении указанного диапазона.

Это дает смысл для разработчиков предоставлять многопоточные / параллельные версии этих функций.

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

Будьте осторожны при создании функторов с сохранением состояния.

2
ответ дан 30 November 2019 в 21:53
поделиться
Другие вопросы по тегам:

Похожие вопросы: