C ++ 14 встречает странную ошибку «использование удаленной функции» - PullRequest
0 голосов
/ 13 сентября 2018

У меня есть простой фрагмент ниже для 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. Пока я хочу вернуть ссылку, почему добавление «ref» здесь вызывает ошибку?
  2. Я не понимаю, почему g ++ рассматривает это как «использование удаленной функции», какая функция удаляется?

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

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

Первая проблема заключается в том, что вы возвращаете ссылку на объект, который больше не будет существовать, когда ваша функция вернется.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 (или значениям в целом).Для этого нужно правильное значение.Этого можно избежать, объявив конструктор, который принимает rvalue ссылки на упакованный тип (в данном случае int), и удалив его.Попытка использовать этот конструктор приведет к ошибке, которую вы видите.Удаленная функция - std::reference_wrapper<int>::reference_wrapper<int>(int&&), которая вызывается преобразованием, введенным компилятором.

0 голосов
/ 13 сентября 2018

std::ref не просто возвращает ссылку, он возвращает Reference Wrapper . В частности, 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;
}
...