Когда функция получает ссылку на rvalue, каков тип этой переменной внутри функции? - PullRequest
1 голос
/ 14 июня 2019

Это вопрос терминологии. Если у меня есть это:

#include <vector>

void g(std::vector<int>&& arg);

void f0(std::vector<int>&& v) {
    static_assert(std::is_same<decltype(v), std::vector<int>&&>::value); // Looks like v is an rvalue reference.
    static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
    return g(std::move(v)); // Fine.
}

тогда какой тип v? Если вы говорите о вызове f0, вы бы сказали «f0 принимает ссылку rvalue» (верно?), Но в пределах f0, v не является ссылкой rvalue, иначе std::move не будет не требуется? Правильно? Но static_assert показал, что это значение, верно?

Аналогично:

void f1(std::vector<int>&& v) {
    static_assert(std::is_same<decltype(v), std::vector<int>&&>::value);
    static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
    return g(v); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'.
    // So is v just a std::vector<int>?
}

Локальные ссылки на rvalue действуют так же:

void f2(std::vector<int>&& v) {
    std::vector<int>&& vv = std::move(v);
    static_assert(std::is_same<decltype(vv), decltype(v)>::value, "They are the same decltype. So being an argument isn't magic.");
    static_assert(std::is_same<decltype(vv), std::vector<int>&&>::value);
    static_assert(std::is_same<decltype((vv)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(vv)>::type, std::vector<int>>::value);
    return g(vv); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'
}

Какая правильная терминология описывает тип v? Правильно ли говорить, что f0 принимает ссылку на rvalue? Если v является ссылкой rvalue, какова терминология, чтобы сказать, что ссылка rvalue не может использоваться для вызова функции, принимающей ссылку rvalue?

Ответы [ 2 ]

3 голосов
/ 14 июня 2019

объявленный тип переменной с именем v равен std::vector<int>&&. Этот тип читается как « ссылка на std::vector».

Имя v может появляться в выражении . Выражения никогда не имеют ссылочный тип [expr.type] / 1 . Но выражения имеют значение категории . Когда имя v появляется в выражении, как в v[0], подвыражение v имеет тип std::vector<int> и его категория значений lvalue . Это касается почти всех id-выражения (выражения, которые являются просто именем).

decltype(v) дает объявленный тип переменной v.

decltype(expression) дает:

  • lvalue-ссылка на тип expression, если expression является lvalue,
  • rvalue-ссылка на тип expression, если expression является xvalue,
  • тип expression, если expression является prvalue.

Более подробная информация приведена в [dcl.dcl] / 1 .

2 голосов
/ 14 июня 2019

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

Да, функция принимает аргумент типа "ссылка на значение std::vector<int>". Эта ссылка может быть инициализирована из выражения rvalue типа std::vector<int> на месте вызова.

Тип выражения v внутри функции, когда вы начинаете пытаться ее использовать, не std::vector<int>&&; эталонный вид "распадов". Это только часть механизма работы ссылок. (В decltype есть некоторая странность в этом отношении.) Для всех намерений и целей вы в конечном итоге получите lvalue выражение типа std::vector<int>. Во всяком случае, на данный момент тип не имеет значения; дело в том, что имя v является lvalue, и чтобы снова превратить его в rvalue, вам нужно std::move.

Но static_assert показал, что это значение, верно?

Неа. «Ссылка на значение» описывает типы типов. «rvalue» является категорией значения. Вы можете удивиться, почему они выбрали такую ​​запутанную терминологию. Я тоже.

...