Как автоматически выводить тип?
У меня есть несколько случаев использования auto
:
auto s = expr; //s is always lvalue
auto & s = expr; //s is always lvalue reference? What if expr is rvalue?
auto && s = expr; //s is perfectly forwarded
Они правда? Если нет, то почему?
2 ответа
Дип правильно, и я хотел бы уточнить.
Прежде всего, вывод из dyp:
Тип выведенный для
auto
в объявлении переменной определяется правила вывода шаблонов аргументов, см. [dcl.spec.auto]/6; с одним исключением: если инициализатор представляет собой список фигурных скобок, выведенный типstd::initializer_list
,
Я объясню.
Первый,
auto s = expr;
Это то же самое, что выводить T
от expr
,
template<class T>
void f(T s);
f(expr);
Правило вывода аргументов шаблона довольно сложное, так как вы имеете дело только с lvalue и rvalue, давайте сосредоточимся на этом.
Вывод аргумента шаблона происходит путем сравнения типа параметра шаблона (назовите его P
, в этом случае P
является T
) и соответствующий аргумент (назовите его A
в данном случае тип expr
).
От 14.8.2.1,
Если P не ссылочный тип:
- Если A является типом массива, тип указателя, полученный стандартным преобразованием массива в указатель (4.2), используется вместо A для вывода типа; иначе,
- Если A является типом функции, то для определения типа используется тип указателя, созданный стандартным преобразованием функции в указатель (4.3); иначе,
- Если A является cv-квалифицированным типом, cv-квалификаторы верхнего уровня типа A игнорируются для вывода типа.
Так что если expr
является массивом или функцией, она будет рассматриваться как указатели, если expr имеет cv-qualification (const
и т.д.), они будут игнорироваться.
Если
P
это cv-квалифицированный тип, cv-квалификаторы верхнего уровняP
тип игнорируется для вывода типа.
Это на самом деле говорит:
const auto s = expr;
s
это const
переменная, но для вывода типа для auto
цели, const
будут удалены
Таким образом, из приведенных выше правил, auto
будет выведен на тип expr
(после некоторого преобразования типа, указанного выше).
Обратите внимание, что когда выражение является ссылкой на T
, он будет скорректирован до T
до предварительного анализа.
Так что expr
is - тип значения rvalue, lvalue или lvalue/rvalue - тип auto
всегда будет тип expr
без ссылки.
auto s1 = 1; //int
int &x = s1;
auto s2 = x; //int
int &&y = 2;
auto s3 = y; //int
Во-вторых, давайте посмотрим на
auto &s = expr;
Это будет так же, как
template<class T>
void f(T &s);
f(expr);
Дополнительное правило из стандарта выглядит следующим образом:
Если
P
является ссылочным типом, тип, на который ссылаетсяP
используется для вывода типа.
Так что вычет авто будет точно таким же, как и без &
, но после auto
тип вычитается, &
добавляется в конец auto
,
//auto &s1 = 1; //auto is deducted to int, int &s1 = 1, error!
const auto &s1 = 1; //auto is deducted to int, const int &s1 = 1; ok!
const int &x = s1;
auto &s2 = x; //auto is int, int &s2 = x; ok!
int &&y = 2;
auto &s3 = y; //auto is int, int &s3 = y; ok!
Обратите внимание, что последний y
это значение. Правило C++: именованная ссылка rvalue является lvalue.
И, наконец:
auto &&s = expr;
Это, несомненно, так же, как
template<class T>
void f(T &&s);
f(expr);
Одно дополнительное правило из стандартного:
Если
P
является rvalue ссылкой на неквалифицированный cv параметр шаблона, а аргумент является lvalue, вместо него используется тип "lvalue ссылка на A"A
для вывода типа.
Это на самом деле говорит, что если expr
является rvalue, правило будет таким же, как и во втором случае (lvalue case), но если expr
является lvalue, тип A
будет lvalue ссылкой на A
,
Примечание из предыдущего объяснения, A
никогда не является ссылкой, потому что тип выражения никогда не является ссылкой. Но для этого особого случая (auto &&
, а также A
является lvalue), ссылка на A
должны быть использованы независимо expr
сам по себе является ссылочным типом или нет.
Пример:
auto &&s1 = 1; //auto is deducted to int, int &&s1 = 1, ok!
int x = 1;
auto &&s2 = x; //x is lvalue, so A is int &, auto is deducted to int &, int & &&s2 = x; ok!
int &&y = 2;
auto &&s3 = y; //y is lvalue, auto is int &, int & &&s3 = y; ok!
Поскольку никто не дает ответа, и я теперь вроде понимаю (спасибо @dyp), я бы просто опубликовал ответ сам. Укажите ошибку, если есть:
auto s = expr;
Если expr
является lvalue, prvalue или любой ссылкой, s
всегда lvalue и копия сделана.
auto& s = expr;
Если expr
является lvalue, ссылкой lvalue или ссылкой rvalue, s
это ссылка на значение. Если expr
Это prvalue, это ошибка и не законно.
auto&& s = expr;
s
отлично переадресован (rvalue станет ссылкой на rvalue и свернется).