2013-05-25 6 views
2

Следующий код работает отлично:Статические переменные в C++ Лямбдами

#include <iostream> 
#include <functional> 

std::function<int (void)> get_incrementer() { 
    return []() { 
     static int count = 0; 
     return count++; 
    }; 
} 

int main() { 
    using std::cout; 

    auto incrementer = get_incrementer(); 

    cout << incrementer() << std::endl; 
    cout << incrementer() << std::endl; 

    return 0; 
} 

Но если вы захватываете локальную переменную по ссылке, а она вдруг результаты к неопределенному поведению, presumely, потому что в время вызова, что расположение в стеке используется другим.

std::function<int (void)> get_incrementer() { 
    int count = 0; 

    return [&count]() { 
     return count++; 
    }; 
} 

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

ответ

6

C++ позволяет это, потому что C++ не является безопасным языком. И хотя этот случай может быть «тривиальным» для проверки (и лично я не согласен с тем, что это тривиально, но я не автор компилятора), есть много других случаев, которые являются не тривиальным, чтобы проверить ,

C++ не относится к коммерческой деятельности исправление Ваш поврежденный код для вас. Он делает точно и только то, что вы говорите, даже если это массово неразумно.

Кроме того, не совсем ясно, что именно вы намерены выполнять этот код. Например, эти два полностью разные вещи:

std::function<int (void)> get_incrementer() { 
    return []() { 
     static int count = 0; 
     return count++; 
    }; 
} 

std::function<int (void)> get_incrementer() { 
    int count = 0; 
    return [count]() mutable { 
     return count++; 
    }; 
} 

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

Какой пользователь пожелал? Не ясно. Поэтому вы не можете просто волей-неволей «исправить» его.

+0

Кажется тривиальным проверить, не лямбда, которая захватывает переменные внутри области действия, чтобы оставить эту область. – Overv

+0

@Overv: «Лямбда» - это не какая-то специальная конструкция; это просто сокращенный способ написания простого класса функторов и создания для него экземпляра. Это означает, что компилятор не может относиться к ним иначе, чем к определенному пользователем классу. Просто потому, что что-то «кажется тривиальным» не делает * его тривиальным. –

+0

Если я хочу получить поведение последнего случая, должен ли я вместо этого создать функтор? – Overv

2

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

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

Использование ссылок на локальные переменные после того, как локальная переменная выходит из области действия, всегда является неопределенным поведением и не принесет никакой пользы, использование лямбда для этого не является особенным.