Почему эта неправильная инициализация std :: function компилируется с использованием MSVC? - PullRequest
0 голосов
/ 20 февраля 2019

Сегодня наткнулся на интересную проблему, начатую моей собственной опечаткой.Я создал лямбду, которая получает ссылку на структуру и неправильно устанавливает ее в std :: function, которая получает аргумент по значению.

Вот более краткая версия:

#include <functional>

struct InputStruct
{
    int i;
    InputStruct(): i(1){}
};

void function_rcv(std::function<bool(InputStruct)> & func_ref)
{
    InputStruct in;
    func_ref(in);
}


int main()
{
    std::function<bool(InputStruct)> my_func = [](InputStruct & in)->bool{return in.i==1;};
    function_rcv(my_func);
}

Проверка с помощью godbolt показывает, что он успешно компилируется с MSVC, но не работает как для Clang, так и для GCC.

Интересно, что использование примитива вместо struct не приводит к компиляции во всех трех компиляторах.

Это ошибка в компиляторе MSVC?

1 Ответ

0 голосов
/ 21 февраля 2019

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

Прежде всего, мне нужно уточнить один аспект std::function: он принимаетфункция (обычно Callable ), сигнатура которой не является идеальным совпадением, но параметры могут быть преобразованы.Рассмотрим:

using intFn = void (int);
void fn(short);

intFn *a = fn;               // doesn't compile
std::function<intFn> b = fn; // compiles!

Здесь intFn тип функции, который имеет параметр int, а функция fn имеет параметр short.Указатель простой функции a не может быть установлен на fn, так как тип параметра отличается (int против short).Но std::function позволяет это, поэтому b может быть установлен на fn.

В вашем примере std::function имеет параметр InputStruct по значению, в то время как лямбда не имеет-const lvalue ссылка InputStruct &.Когда std::function std::forward s его параметр, он становится значением xvalue, которое не может быть привязано к ссылочному параметру lvalue лямбды.Вот почему стандартные соответствующие компиляторы не принимают этот код.

Почему MSVC принимает этот код?Поскольку он по умолчанию имеет несоответствующее поведение: он позволяет привязывать временные классы (и значения x) к неконстантным ссылкам lvalue.Вы можете отключить это поведение с помощью /Zc:referenceBinding (или более старой опции /Za).Если вы используете этот переключатель, MSVC отклонит ваш пример.

...