Почему эта функция C ++ lamba не скомпилируется? - PullRequest
3 голосов
/ 14 января 2011

Почему это не скомпилируется:

int myVar = 0;
myVar ? []()->void{} : []()->void{};

со следующей ошибкой:

Ошибка 2 ошибка C2446: ':': нет преобразования из 'red_black_core :: `anonymous-namespace' :: 'в red_black_core :: anonymous-namespace ::

Хотя это соответствует правильно:

void left()
{}
void right()
{}

int myVar = 0;
myVar ? left() : right();

Ответы [ 5 ]

9 голосов
/ 14 января 2011

Тип возврата оператора?: Должен быть выведен из его двух операндов, и правила для определения этого типа довольно сложны. Лямбды не удовлетворяют их, потому что они не могут быть обращены друг к другу. Поэтому, когда компилятор пытается выяснить, каков результат этого?:, Тогда не может быть результата, потому что эти две лямбды не могут быть преобразованы друг в друга.

Однако, когда вы пытаетесь вычислить функции, вы фактически вызываете их, но вы не вызываете лямбды. Поэтому, когда вы вызываете функции, они обе имеют void, поэтому тип возвращаемого значения?: Void.

Это

void left()
{}
void right()
{}

int myVar = 0;
myVar ? left() : right();

эквивалентно

int myVar = 0;
myVar ? [](){}() : [](){}();

Обратите внимание на дополнительный () на конце - я на самом деле назвал лямбду.

То, что у вас было изначально, эквивалентно

compiler_deduced_type var;
if (myVar)
    var = [](){};
else
    var = [](){};

Но - не существует типа, который может быть как лямбдами. Компилятор вполне может создавать разные лямбды-типы.

EDIT:

Я кое-что вспомнил. В последнем стандартном проекте лямбда-выражения без перехватов могут быть неявно преобразованы в указатели функций с одной и той же сигнатурой. То есть в приведенном выше коде compiler_deduced_type может быть void (*) (). Однако я точно знаю, что MSVC не включает это поведение, потому что оно не было определено в то время, когда они реализовали лямбда-выражения. Вероятно, именно поэтому GCC позволяет это делать, а MSVC этого не делает - лямбда-поддержка GCC существенно новее, чем поддержка MSVC.

5 голосов
/ 14 января 2011

Правила условного оператора в черновике n3225 гласят в одной точке

В противном случае результат является prvalue.Если второй и третий операнды не имеют один и тот же тип и оба имеют (возможно, cv-квалифицированный) тип класса, разрешение перегрузки используется для определения преобразований (если они есть), которые должны применяться к операндам (13.3.1.2, 13.6),Если не удается разрешить перегрузку, программа работает некорректно.В противном случае, определенные таким образом преобразования применяются, и преобразованные операнды используются вместо исходных операндов в оставшейся части этого раздела.

До этой точки, любая другая альтернатива (например, преобразовать одинк другому операнду) не удалось, поэтому теперь мы будем делать то, что говорится в этом абзаце.Преобразования, которые мы будем применять, определяются разрешением перегрузки путем преобразования a ? b : c в operator?(a, b, c) (воображаемый вызов функции так называемой функции).Если вы посмотрите, каковы кандидаты в мнимые operator?, вы найдете (среди прочих)

для каждого типа T, где T - указатель, указатель на член или тип перечисления с областью видимостиСуществуют операторные функции-кандидаты вида

T operator?(bool, T , T );

И это включает кандидата, для которого T является типом void(*)().Это важно, потому что лямбда-выражения дают объект класса, который можно преобразовать в такой тип.В спецификации говорится:

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

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

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

3 голосов
/ 14 января 2011

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

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

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

2 голосов
/ 14 января 2011

Оба фрагмента кода прекрасно компилируются с GCC 4.5.2.

Возможно, ваш компилятор не поддерживает (или частично / не работает) функции C ++ 0x, такие как лямбда?

0 голосов
/ 14 января 2011

Он не может не скомпилироваться.Работает просто отлично. Возможно, в вашем компиляторе не включен C ++ 0x.

Редактировать: Теперь к исходному вопросу добавлено сообщение об ошибке!Кажется, у вас есть поддержка C ++ 0x, но она не завершена в вашем компиляторе.Это неудивительно.

Код по-прежнему действителен для C ++ 0x, но я рекомендую использовать функции C ++ 0x только тогда, когда это действительно необходимо, до тех пор, пока он не станет стандартизированным и не будет обеспечена полная поддержка в целом ряде наборов инструментов.,У вас есть жизнеспособная альтернатива C ++ 03, которую вы дали в своем ответе, и я предлагаю пока использовать ее.

Возможное альтернативное объяснение:

Также обратите вниманиечто ты, вероятно, не написал то, что на самом деле хотел написать.[]()->void{} лямбда.[]()->void{}() выполняет лямбду и вычисляет ее результат.В зависимости от того, что вы делаете с этим результатом, ваша проблема может заключаться в том, что результат вызова вашей лямбды равен void, и вы не можете многое сделать с void.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...