Неопределенное поведение с закрытием C ++ 0x: I - PullRequest
4 голосов
/ 04 апреля 2011

Рассмотрим пример:

#include <iostream>
#include <functional>     // std::function
#include <vector>        // std::vector
#include <algorithm>    // std::for_each

int main(){

    auto adder = [](int x) {
        return [&](int y) { 
            return x+=y; 
        }; 
    };

    std::vector < std::function<int(int)> > vec;

    vec.push_back(adder(1));
    vec.push_back(adder(10));

    std::for_each(vec.begin(), vec.end(), [](std::function<int(int)> f){std::cout << f(33) << " ";});
    std::cout << std::endl;
}

Можно ожидать, что целые числа 34 и 43 43 и 76 , но вместо этого gcc 4.6.0 выдает "внутренняя ошибка компилятора: ошибка сегментации". Что не так с кодом?

Редактировать: Обсуждается несколько других примеров здесь .

Ответы [ 3 ]

2 голосов
/ 04 апреля 2011

(Изменить: это, конечно, не объясняет ICE; я слишком быстро прочитал исходный вопрос)в этом коде лямбда-выражения, возвращаемые функцией adder, содержат висячие ссылки на переменную x, которая больше не существует.Захватите копию ([=] или [i]) вместо ссылки ([&]), и все должно работать.

0 голосов
/ 05 апреля 2011

Вы просто полностью упускаете суть.Потребность в std::function очень и очень очевидна.

  1. Все лямбды имеют уникальный тип во время компиляции
  2. Вы хотите, чтобы вектор содержал любой функциональный объект во время выполнения.
  3. Следовательно, требуется какое-то стирание типа, что и делает работа std::function.

Как же вообще можно создать вектор, который изменяется во время выполнения?факт времени компиляции, как тип, содержащийся в нем?Это просто логически невозможно - если вы не используете абстракцию, такую ​​как std::function.

Конечно, если вам нужен только один лямбда-тип внутри, тогда вам вообще не нужен std::function.Хотя это относительно редко.

int main() {
    auto adder = [](int x) {
        return [=](int y) {
            return x + y;
        };
    };
    // alternatively- you MUST copy the argument as it will cease to exist
    // but once it's in the lambda, you can use "mutable" to allow you to
    // modify the copy that each lambda has.
    /*
    auto adder = [](int x) {
        return [=](int y) mutable {
            return x += y;
        };
    };
    */
    std::vector<decltype(adder(0))> adders;
    adders.emplace_back(adder(0));
    adders.emplace_back(adder(1));

    std::for_each(adders.begin(), adders.end(), [](decltype(*adders.begin())& ref) {
        std::cout << ref(33);
    });
    std::cin.get();
}

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

0 голосов
/ 05 апреля 2011

Кажется, что в вашем примере trailing-return-type нельзя опускать.Вот выдержка из стандарта (лямбда-выражения 5.1.2):

Если лямбда-выражение не включает в себя конечный тип возврата, это как если бы конечный тип возврата обозначаеттип: - если составной оператор имеет форму {атрибут-спецификатор-возвращаемое выражение;} тип возвращаемого выражения после преобразования lvalue-to-rvalue (4.1), преобразования массива в указатель (4.2) и преобразования функции в указатель (4.3);- иначе, void.

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

auto adder = [] (int x) -> std::function<int (int)> {
  return [=]( int y ) {
    return x + y;
  };
};
...