Почему ссылка-значение в качестве возвращаемого типа не может быть инициализацией неконстантной ссылки? - PullRequest
0 голосов
/ 26 мая 2018

Я прочитал этот вопрос и знаю, что rvalue referenec является lvalue.

Однако для этого кода, пример 1,

int &&fun() {
    return 1;
}

int main() {
    int &a = fun();
}

Когда яскомпилируйте его:

 error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'

Таким образом, компилятор C ++ сообщает мне, что тип возвращаемого значения fun является rvalue.

Как ссылка на rvalue становится rvalue?

Я думаю, что компилятор должен обрабатывать ссылку на lvalue и ссылку на rvalue одинаково, но этот код, пример 2,

int & fun(){
    int b;
    return b;
}
int main(){
    int & a=fun();
}

может быть скомпилирован (тем не менее, я получаю предупреждение).

Я думаю, что, возможно, тип возвращаемого значения fun изменился в какой-то момент.

Попытка скомпилировать пример 3:

int &&fun() {
    return 1;
}

int main() {
    decltype(fun()) b = 1;
}

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

Итак, почему ссылка на rvalue становится значением rvalue?

Вот пример 4:

int &&a = 1;
int &b = a;

Он компилирует и сообщает нам, что rvalue-ссылка может быть связана с lvalue-ссылкой.

Теперь, как насчет этих двух вопросов:

  1. В примере 1 это fun() rvalue?
  2. В примере 1 fun() является ли rvalue ссылкой?

Пример 3 говорит нам, что fun() является ссылкой rvalue, а пример 4 сообщает нам rvalueссылка может быть привязана к ссылке lvalue (как const, так и не const).Тогда почему fun() из примера 1 не может быть привязано к ссылке lvalue?

Пример 4 также указывает, что ссылка rvalue является lvalue, но ошибка компиляции в примере 1 говорит нам, что fun()там, который в качестве примера является ссылкой на rvalue в примере 3, является rvalue.Итак, является ли rvalue ссылкой на lvalue или rvalue?

Если причина в том, что fun() является просто выражением, которое существует временно и сразу исчезнет, ​​почему fun() из примера 2 не может рассматриваться какRvalue в то время как это также просто выражение без имени?Какая разница между выражением функции, возвращающей ссылку lvalue и ссылку rvalue?

Ответы [ 5 ]

0 голосов
/ 27 мая 2018

Ключ в том, что категория значений выражения зависит не только от его типа, например,

int&& a = 1;
int&& fun();
// int&& ri = a; // ill-formed, the expression a is of type int&&, but is an lvalue
int&& ri = fun(); // ok, the expression fun() is of type int&&, and is also an rvalue

Кроме того, как замечает rustyx в его ответ определение функции

int && fun(){
    return 1;
} 

может привести к неопределенному поведению, поскольку временный объект будет уничтожен сразу после выполнения оператора return.

0 голосов
/ 27 мая 2018

Я знаю, что ссылка на rvalue является lvalue.

Вы говорите о двух разных вещах: тип и значение категории .например,

int&& ri = 0; // ri's type is rvalue reference (to int)
              // ri's value category is lvalue; it's a named variable.

Учитывая ваш 1-й пример, то, что fun() возвращает, представляет собой xvalue, которое принадлежит rvalue.

Следующие выражения являются выражениями xvalue:

  • вызов функции или перегруженное выражение оператора, тип возвращаемого значения которого является rvalue ссылкой на объект, например std::move(x);

then,

int &a = fun(); // fails; lvalue-reference can't bind to rvalue

Во втором примере fun() возвращает значение lvalue,

Следующие выражения являются выражениями lvalue:

  • вызов функции или перегруженное выражение оператора, чье выражениетип возвращаемого значения - ссылка lvalue, например std::getline(std::cin, str), std::cout << 1, str1 = str2 или ++it;

затем

int & a=fun(); // fine; lvalue-reference could bind to lvalue

В 3-мобразец,

decltype(fun()) b = 1; // the return type of fun() is rvalue-reference;
                       // this has nothing to do with the value category of its return value
                       // b's type is rvalue-reference too, btw its value category is lvalue

В 4-м образце,

int &&a = 1; // fine; rvalue-reference could bind to rvalue
             // a's type is rvalue-reference, its value category is lvalue
int &b = a;  // fine; lvalue-reference could bind to lvalue
             // b's type is lvalue-reference, its value category is lvalue
0 голосов
/ 26 мая 2018

Я думаю, что вы смешиваете rvalue и rvalue reference.В вашем первом примере

int && fun(){
    // 1 is an rvalue so it can be bound to an rvalue reference
    // this will produce undefined behavior though because you
    // a returning a dangling reference to an temporary that will
    // go out of scope at the end of this function
    return 1;
}
int main(){
    // you are trying to take a reference to a temporary object.
    // this is (deliberately) not valid
    int & a=fun();

    // One legal way of doing this is by declaring your reference const:
    const int& b = fun(); 
    // because this extends the lifetime of the temporary object returned
    // by fun() to match the lifetime of the reference.
}

Во втором примере:

int & fun(){
    // you have allocated an new int in the free store so the 
    // lifetime of this int is until the main exits. The return
    // type here is an lvalue that can be safely bound to an 
    // lvalue reference
    return *(new int);
}
int main(){
    // binding lvalue reference to lvalue this is ok
    int & a=fun();
}

В вашем третьем примере

int && fun(){
    // 1 is an rvalue and can be bound to an rvalue reference
    return 1;
}
int main(){
    // decltype(fun()) is equal to int&& so it's ok to bind
    // an rvalue reference to an rvalue
    decltype(fun()) b=1;
}
0 голосов
/ 26 мая 2018

Прежде всего, этот код демонстрирует неопределенное поведение:

int && fun(){
    return 1;
}

Здесь вы возвращаете висячую ссылку на 1, которая выходит за рамки.

Как ссылка rvalue становится rvalue?

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

Тогда будет полезно перейти к правилам инициализации ссылок:

Правило инициализации первой ссылки гласит, что ссылкаможет быть инициализирован («связан») со значением , совместимым с эталоном .Это означает, что

  • int& может связываться с int&
  • int&& может связываться с int&&
  • const int& может связываться с int&

В этом случае фактическое упомянутое значение с правой стороны не извлекается, но напрямую связано с новой ссылкой.Обратите внимание, что int& не совместим с int&&, это разные типы.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * '' * ''Ссылка rvalue (int&&) может связываться с:

  • xvalue или prvalue
  • в качестве последнего средства к чему-либо еще

В случае последнегоссылка связывается с результатом выражения.В случае const int& x = fun() результат вызова fun() сначала будет «материализован» (извлечен), а затем его значение будет привязано к ссылке.

Но длячтобы это произошло, ссылка lvalue должна быть const.Вот почему в ошибке говорится, что не-const int& не может связываться с int, потому что int является результатом оценки fun().

0 голосов
/ 26 мая 2018

Неконстантные ссылки не могут привязываться к rvalues, это так просто.

int & a=fun();

не работает, потому что a - неконстантная ссылка, а fun() - выражение rvalue.
Во втором случае fun() возвращает неконстантную ссылку lvalue, которая, конечно, может связываться с другой неконстантной ссылкой.

decltype(fun()) b=1;

работает, потому что decltype(fun()) равен int && итаким образом, может связываться с целым литералом 1.


В примере 1 fun() является значением?

Да.

В примере 2 fun() является ли rvalue ссылкой?

Нет, это lvalue ссылка.

Пример 3 говорит нам, что fun() этоссылка на rvalue и пример 4 говорят нам, что ссылка на rvalue может быть связана со ссылкой на lvalue (как const, так и non-const).Тогда почему fun() из примера 1 не может быть привязано к ссылке lvalue?

Поскольку функция fun возвращает ссылку rvalue, а сама fun() является выражением rvalue.fun() является значением r.

Пример 4 также указывает на то, что ссылка на значение rvalue является lvalue, но ошибка компиляции в примере 1 говорит нам, что fun() там, что оказывается значением rvalueСсылка в примере 3 является значением.Итак, является ли ссылка rvalue lvalue или rvalue?

Ссылка rvalue является lvalue.

Если причина в том, что fun() является просто выражением, которое существуетвременно и сразу же умрет, почему fun() в примере 2 не считается значением, в то время как это также просто выражение без имени?Какая разница между выражением функции, возвращающей ссылку lvalue и ссылку rvalue?

Поскольку в примере 2 fun() является lvalue.Начиная с N4296, §3.10 / 1.1:

[...] результатом вызова функции, тип возвращаемого значения которой является ссылкой на lvalue, является lvalue.


Что касается предупреждения, которое вы получаете, например, 2, вы должны показать точное сообщение.Возможно, это только потому, что вы возвращаете ссылку на локальную переменную.Локальные переменные имеют ограниченное время жизни, поэтому обращение к ним за пределами срока их действия является неопределенным поведением, поэтому следует предупреждение.

...