Подтвердить что ты не робот

Тип объекта, захваченного референцией внутри лямбда

Следующий код работает с gcc

#include <map>

int main() {
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict)::value_type bar;
    };
}

Но для msvc Мне нужно дополнительно использовать std::remove_reference

#include <map>
#include <type_traits>

int main() {
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        std::remove_reference_t<decltype(dict)>::value_type bar;
    };
}

В противном случае я получаю ошибку:

error C2651: 'std::map<int,double,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> &': left of '::' must be a class, struct or union

Какой компилятор показывает правильное поведение в соответствии со стандартом?

обновление:

Для msvc decltype(dict) действительно является ссылкой, так как следующий код

#include <map>

int main()
{
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict) foo;
    };
}

ошибки с

error C2530: 'foo': references must be initialized

Если это действительно неправильное поведение, это может привести к неприятным ошибкам, например, обвисшим ссылкам, когда код скомпилирован с msvc.

#include <map>

std::map<int, double> return_a_map()
{
    std::map<int, double> result;
    return result;
}

int main()
{
    std::map<int, double> dict;
    const auto lambda = [&]()
    {
        decltype(dict) foo = return_a_map();
        // foo is a dangling reference in msvc
    };
}
4b9b3361

Ответ 1

Не существует специального правила, касающегося неконтегрированных приложений decltype (т.е. [expr.prim.lambda]/20 не применяется). Таким образом, мы просто возвращаемся к обычному определению decltype, в котором указывается, что если операнд является id-выражением, полученный тип является только объявленным типом объекта и не является ссылочным типом. Следовательно, VС++ ошибочен.

NB: неважно, захвачен ли dict или нет, потому что & para; 17:

Каждое id-выражение внутри составного оператора лямбда-выражения , которое является неприемлемым (3.2) объекта, захваченного копией, преобразуется в доступ к соответствующему неназванному элементу данных из Тип закрытия. [Примечание: Идентификатор, не являющийся неприемлемым, относится к исходному объекту, никогда не принадлежащему члену тип закрытия. Более того, такое id-выражение не вызывает неявный захват объекта. - конец примечание]

decltype never odr - использует любой из своих операндов или супеперад. Это правило иногда становится довольно проблематичным, например. как показано в ключевой вопрос 958:

int f (int&);
void* f (const int&);

int main()
{
   int i;
   [=] ()-> decltype(f(i)) { return f(i); };
}

Здесь decltype(f(i)) использует не const i из охватывающей области. Однако, поскольку лямбда не mutable, i в теле на самом деле const, поэтому тип возвращаемого возврата неверен. CWG пришел к выводу, что это возникает слишком редко, чтобы стоить решения.