2016-08-09 9 views
3

Использование лямбда-функции в C++ с переменными, захваченными значением, означает copy of the value.Будет ли C++ лямбда действительно делать копии параметров, захваченных копией?

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

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

void loadavg_file::add(loadavg_item const & new_item) 
{ 
    auto const & it(std::find_if(
      f_items.begin(), 
      f_items.end(), 
      [new_item](auto const & item) 
      { 
       return (item.f_address == new_item.f_address); 
      })); 

    if(it == f_items.end()) 
    { 
     f_items.push_back(it); 
    } 
    else 
    { 
     // replace existing item with new avg and timestamp 
     it->f_timestamp = new_item.f_timestamp; 
     it->f_avg = new_item.f_avg; 
    } 
} 

Будет ли цикл быть оптимизированными и привести к абсолютно никакой копии new_item?

+6

Гарантия не известна. Хороший компилятор * может * оптимизировать копию, но это не требуется. –

+5

Единственное, что вы знаете, это код, который должен вести себя так, как он. Если нет никакой разницы между вложением всей вещи и не созданием копий/объектов и тем, что вы написали, тогда компилятор мог это сделать. Будет ли он? скомпилировать его и проверить сборку. – NathanOliver

+2

Этот вопрос кажется мне более интересным, чем другим двум комментаторам. AFAIK, компилятору разрешено выдавать копии только в определенных условиях, и я не знаю, является ли захват лямбда одним из них. (Это не имеет значения, когда копии тривиальны, но OP, вероятно, не волнует, если копии были тривиальными.) – zneak

ответ

6

Если копия конструктор new_item (т.е. loadavg_item::loadavg_item(loadavg_item const&)) имеет, кроме распределения памяти наблюдаемых эффектов, то эти эффекты должны быть соблюдены, чтобы произойти (если, y'know, вы на самом деле сделать усилие, чтобы наблюдать за ними).

Это потому, что вы можете быть в зависимости от этих побочных эффектов, и это не тот контекст, в котором разрешено копирование; копирование разрешено только (и, наконец, санкционировано), когда возвращает значение от функции. С другой стороны, разрешение выделения памяти разрешено в любом месте (с учетом правил в [expr.new]/10); Особенно хорош этот кланг.

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

Если конструктор копирования new_item не является встроенным, то сборка единицы перевода может отображать вызов конструктора копирования в качестве символа, но оптимизация времени соединения (LTO) может по-прежнему устранять этот вызов, если ссылка оптимизатор времени может вывести, что конструктор копирования не имеет наблюдаемых побочных эффектов.

+0

Будет ли это означать, что использование ссылки более вероятно увеличивает вероятность получения оптимизации? Другими словами, как программисты на С ++, следует ли использовать ссылки почти во всех случаях для целей оптимизации (если, очевидно, функция лямбда действительно не вносит изменения в копию объекта)? –

+1

@Alexis: Я бы не стал так говорить. Ссылка гарантирована, чтобы избежать копирования, а также, вероятно, убить другие оптимизации, потому что компилятор должен обрабатывать случаи с псевдонимом. –

1

Да, a копия две копии (потому что лямбда передана по значению, ее члены снова копируются) необходимы для вашего кода.

Если все дерево вызовов (find_if, конструктор копирование, деструктор, operator==, любые функции тех вызовов) состоит из видимых функций и компилятора выбрать встраивать их, возможно, что дальнейшие оптимизации, такие как здравое подвыражения-элиминирование может сократить или исключить стоимость выполнения этих копий.

В процессе, компилятор должен будет доказать, что

  • Значение копии не изменяется (потому что такие изменения не должны распространяться на оригинал)
  • Конструктора копирования не имеет стороны последствия.
  • Деструктор не имеет побочных эффектов.
  • Ни одна из функций не зависит от личности объекта.
  • Срок службы копии не может превышать срок службы объекта, на который указывает этот параметр.
  • Нет других псевдонимов для объекта, на который указывает этот параметр, или такие другие псевдонимы не будут использоваться для модификации указанного объекта во время использования лямбда, включая обращения из других потоков (если синхронизировано).

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

auto const & it(std::find_if(
     f_items.begin(), 
     f_items.end(), 
     [key = new_item.f_address](auto const & item) 
     { 
      return (item.f_address == key); 
     }));