Это явно игрушечный пример, но допустим, у меня есть N функции как это:
void one(const int param) {
const auto func = [=](){ return 13 == param; };
}
void two(const int param) {
const auto func = [=](){ return 13 == param; };
}
И так далее; все они имеют идентичные лямбда захвата. Можно ли иметь 1 экземпляр лямбда, который всегда захватывает param
функции, в которой он находится, а не N случаи? Может быть, в качестве дополнительного вопроса, который я должен задать, компилятор уже распознает репликацию и упростит их до одного экземпляра?
К сожалению, вы получите несколько типов с этим решением. [Expr.prim.lambda.closure] / 1 говорится, что
Тип лямбда-выражение (который также является типом объекта замыкания) является уникальный, безымянный тип несоединимого класса, называемый типом замыкания, свойства которого описаны ниже.
акцент мой
Так что каждый
const auto func = [=](){ return 13 == param; };
это собственное выражение, поэтому вы получаете новый уникальный тип, даже если они синтаксически одинаковы.
То, что вы могли бы сделать, это перевести повторение в функтор, и тогда у вас будет только один определенный класс.
class compare
{
int val;
public:
compare(int val) : val(val) {}
bool operator() const { return val = 13; }
};
и тогда ваши функции станут
void one(const int param) {
const auto func = compare{param};
}
void two(const int param) {
const auto func = compare{param};
}
Вы можете просто сделать функцию, которая возвращает лямбду:
auto make_lambda(int param) {
return [=](){ return 13 == param; };
}
bool one(const int param) {
return make_lambda(param)();
}
bool two(const int param) {
return make_lambda(param)();
}
Обе функции будут использовать один и тот же сгенерированный класс (но не один и тот же экземпляр). Это сгенерированный код (Получено с помощью C ++ Insights):
__lambda_2_12 make_lambda(int param)
{
class __lambda_2_12
{
public: inline /*constexpr */ bool operator()() const
{
return 13 == param;
}
private:
int param;
public: __lambda_2_12(int _param)
: param{_param}
{}
} __lambda_2_12{param};
return __lambda_2_12;
}bool one(const int param)
{
return make_lambda(param).operator()();
}bool two(const int param)
{
return make_lambda(param).operator()();
}
Синтезированный тип замыкания для лямбды является уникальным и определяется в точке определения, как указано в [expr.prim.lambda.capture]/2
:
Тип замыкания объявляется в самой маленькой области блока, области класса или области пространства имен, которая содержит соответствующее лямбда-выражение […]
и захват параметра функции используется для создания нестатического члена данных для уникальный Тип замыкания введен в область действия функции: [expr.prim.lambda.capture]/10.2
:
Для каждого объекта, захваченного копией, в типе замыкания объявляется неназванный элемент не статических данных. Порядок объявления этих членов не определен […]
каждый введенный тип замыкания будет отличаться, и их члены зависят от того, что зафиксировано в момент определения.
Вы всегда будете отличаться типы, но вы можете не стать отчетливым код для каждого использования. Это работа для компоновщика. Линкер MSVC, а также экспериментальный gold
компоновщик, выполните то, что MSVC называет «сворачивание КОМДАТА» (я не знаю, как это называет золото), которое идентифицирует идентичные функции внутри и между единицами перевода и объединяет их в одну.