C++ 14 обнаруживает странную ошибку «использование удаленной функции»

У меня есть простой фрагмент ниже для g ++ 7.3.1 -std = C++ 14

#include<functional>
#include<iostream>
using namespace std;
struct S{
    int m_i = 2;
    auto& get2(){return ref(m_i);}
};
int main(){
    S s;
    s.get2()=4;
    cout<<s.m_i<<endl;
    return 0;
}

Он компилируется с ошибкой, как показано ниже:

error: cannot bind non-const lvalue reference of type ‘std::reference_wrapper<int>&’ to an rvalue of type ‘std::reference_wrapper<int>’
    auto& get2(){return ref(m_i);}
                        ~~~^~~~~
In function ‘int main()’:
error: use of deleted function ‘std::reference_wrapper<_Tp>::reference_wrapper(_Tp&&) [with _Tp = int]’
    s.get2()=4;
            ^
In file included from /usr/include/c++/7/bits/std_function.h:44:0,
                from /usr/include/c++/7/functional:58,
                from xxx.cpp:1:
/usr/include/c++/7/bits/refwrap.h:338:7: note: declared here
    reference_wrapper(_Tp&&) = delete;
    ^~~~~~~~~~~~~~~~~

Я знаю, что при удалении оболочки "ref" программа компилируется. Я просто хочу знать, в чем проблема с использованием здесь "ref"?

  1. Если я хочу вернуть ссылку, почему добавление здесь ссылки вызывает ошибку?
  2. Я не понимаю, почему g ++ увидел в этом «использование удаленной функции», а какая функция удаляется?

Что на самом деле неясно в сообщении об ошибке?

πάντα ῥεῖ 13.09.2018 19:10
get2 возвращает ссылку. Это проблема, потому что 1) это будет висящая ссылка (экземпляр, на который она ссылается, выходит за пределы области действия в конце функции) и 2) вы не можете привязать ссылку, не относящуюся к const, к временному объекту, подобному тому, который получено из ref(m_i);. Просто вернитесь по значению.
François Andrieux 13.09.2018 19:13

@ FrançoisAndrieux Почему болтается?

Rakete1111 13.09.2018 19:14

есть ли какая-то реальная проблема, которую вы пытаетесь решить? Кажется, ваш пример предназначен только для воспроизведения ошибки, но в остальном я не понимаю, почему вы написали это

463035818_is_not_a_number 13.09.2018 19:15

@ Rakete1111 Функция возвращает ссылку на временный std::reference_wrapper.

François Andrieux 13.09.2018 19:15
0
5
707
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

std::ref не просто возвращает ссылку, он возвращает Справочная оболочка. В частности, std::reference_wrapper<T>. Итак, когда вы пишете return std::ref(m_i);, вы не возвращаете int&, вы возвращаете std::reference_wrapper<int> &, ссылку на висячий объект, который сам содержит ссылку.

Просто верните член как есть; компилятор поймет, что это ссылка.

#include<functional>
#include<iostream>
//Don't use using namespace std;, it's bad practice

struct S{
    int m_i = 2;
    auto& get2(){return m_i;}
};
int main(){
    S s;
    s.get2()=4;
    std::cout<<s.m_i<<std::endl;
    return 0;
}

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

#include<functional>
#include<iostream>

struct S{
    int m_i = 2;
    int& get2(){return m_i;}
};
int main(){
    S s;
    s.get2()=4;
    std::cout<<s.m_i<<std::endl;
    return 0;
}
Ответ принят как подходящий

Есть ряд проблем с кодом, которым вы поделились.

Первая проблема заключается в том, что вы возвращаете ссылку на объект, которого больше не будет, когда ваша функция вернется. std::ref возвращает std::reference_wrapper, который выходит за рамки. std::reference_wrapper - это объект, который предоставляет некоторые функции ссылочного типа, но при этом ведет себя как объект. Он имеет семантику перемещения, поэтому правильным использованием будет возврат std::reference_wrapper по значению.

#include<functional>
struct S {
    int m_i = 2;
    auto get2() { return std::ref(m_i); }
};

Хотя, как отмечали другие, std::ref здесь на самом деле не нужен. Вы можете просто вернуть ссылку на m_i напрямую. Если пользователю действительно нужен std::reference_wrapper для этого int, он может вызвать в std::ref самостоятельно, указав ссылку на m_i, которую вы предоставили.

struct S {
    int m_i = 2;
    int & get2() { return  m_i; }
};

Но это не единственная проблема вашего кода. Предполагая, что вы исправите первую проблему и вернете свой std::reference_wrapper по значению, вы по-прежнему неправильно используете std::reference_wrapper::operator=. Этот оператор не присваивает новое значение указанному объекту. Вместо этого он повторно привязывает ссылку к другому экземпляру. Правильным использованием будет использование get() для получения надлежащего назначаемого int& от std::reference_wrapper:

#include<functional>
#include<iostream>
struct S {
    int m_i = 2;
    auto get2() { return std::ref(m_i); }
};

int main() {
    S s;
    s.get2().get() = 4;
    std::cout << s.m_i << std::endl;
    return 0;
}

Но как насчет сообщения об ошибке? Компилятор видит, что единственный operator=, существующий для std::reference_wrapper, - это тот, который принимает другой std::reference_wrapper. Таким образом, он пытается неявно преобразовать 4 в std::reference_wrapper единственным возможным способом, пытаясь использовать конструктор, который принимает единственный аргумент. Однако невозможно создать std::reference_wrapper с таким литералом, как 4 (или rvalues ​​в целом). Для ссылки требуется правильное lvalue. Этого можно избежать, объявив конструктор, который принимает ссылки rvalue на обернутый тип (в данном случае int), и удалив его. Попытка использовать этот конструктор приведет к ошибке, которую вы видите. Удаленная функция - это std::reference_wrapper<int>::reference_wrapper<int>(int&&), которая вызывается преобразованием, введенным компилятором.

Отличное объяснение!

Troskyvs 13.09.2018 20:24

Другие вопросы по теме