Как передать временный вектор по константной ссылке?

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

typedef const std::vector<int>& t;

class SomeClass
{
    t data;
public:
    SomeClass(t _data) : data(_data) {}
    void disp()
    {
        for (auto v : data)
            std::cout << v << ", ";
        std::cout << std::endl;
    }        
};

int A = 1;
int B = 2;

SomeClass f = SomeClass( {A, B} );
f.disp();

A = 456;
f.disp();

5 ответов

Решение

Исходя из вашего кода, похоже, что вы хотите, чтобы можно было обновлять элементы вектора извне класса.

Однако, прежде чем перейти к этому, есть проблема, что вы храните ссылку на временный объект (vector сделано из {1, 2}). Этот объект освобождается при вызове SomeClass( {A, B} ) завершается.

Есть три способа решить эту проблему:

  1. Если вы знаете время жизни SomeClass объект, вы можете иметь копию к вектору вне SomeClass пример f, Например, если f будет существовать только на время функции, которая его создала, вы можете сделать что-то вроде

    std::vector<int>   aVector({A, B});
    SomeClass          f = SomeClass(aVector);
    

    Функция создания "владеет" aVector, а также f имеет ссылку на это.

  2. Вы можете иметь SomeClass Экземпляр сделать копию вектора. Для этого вы можете передать вектор по ссылке, но сделать копию в конструкторе:

    class SomeClass
    {
        std::vector<int>    data;
      public:
        SomeClass(const std::vector<int> &_data) : data(_data) {}
        ...
    }
    

    f теперь "владеет" своей собственной копией вектора. Однако теперь вы не сможете изменить значение элементов вне объекта, поэтому вы можете хранить указатели внутри вектора (std::vector<int*>) или предоставить метод в классе для изменения содержимого, если это возможно.

  3. Вы можете использовать семантику перемещения (C++11), чтобы убедиться, что внутреннее хранилище вектора аргумента не копируется, а вместо этого переназначается SomeClass::data во время строительства:

    class SomeClass
    {
        std::vector<int>    data;
      public:
        SomeClass(std::vector<int> &&_data) : data(std::move(_data)) {}
        ...
    }
    

    Как прежде, f "владеет" вектором. Опять же, невозможно изменить содержимое f.data снаружи объекта, поэтому вы захотите сохранить указатели на вектор или предоставить метод.

Ни в одном из этих случаев, как написано выше, вы не сможете изменить содержимое вектора, обновив A, Это потому, что копия A создается при создании вектора, и эта копия будет сохранять значение 1,

В случаях (2) и (3), чтобы иметь возможность обновить вектор путем обновления AВы должны хранить указатели в векторе, как уже упоминалось. Вы также можете сделать это в случае (1). Обратите внимание, что при хранении указателей вы должны убедиться, что память, на которую они указывают, сохраняется до тех пор, пока вы можете использовать указатели - по сути, та же проблема владения, что и при решении самого вектора.

В случае (1) вы также можете обновить содержимое вектора, обновив aVector[0], так как f имеет ссылку на aVector,

Я бы сказал, использовать указатели и держать реальный вектор вместо ссылки в вашем классе

#include <iostream>
#include <vector>

int main()
{
//This is now a vector type, not a reference to vector. Also, it contains
// pointer to int instead of a copy.
typedef const std::vector<int *> t; 

class SomeClass
{
    t data;
public:

// We pass the vector by reference here.

    SomeClass(t & _data) : data(_data) {}
    void disp()
    {
        for (auto v : data)
            std::cout << *v << ", ";
        std::cout << std::endl;
    }        
};

int A = 1;
int B = 2;

// We have to pass the address of A and B here.
SomeClass f = SomeClass( {&A, &B} );
f.disp();

A = 456;
f.disp();
}

Тем не менее, поскольку мы держим указатели на A и B (которые являются локальными), как только A и B будут отброшены (в основном следующее '}'), вектор будет содержать указатели на недопустимые значения, и это BAD.

В вашем примере ваш класс отбрасывается с A и B, так что все в порядке, но если ваш класс выжил дольше, чем A и B, поведение disp() было бы неопределенным (вероятно, SIGSEGV).

Есть три ошибки.

  1. Это не списки инициализаторов.
  2. f не назначен, как вы ожидали.
  3. данные не имеют адреса значений

Так:

#include <iostream>
#include <string>
#include <vector>

using namespace std;
typedef const std::vector<int*>& t;

class SomeClass
{
    t data;
public:
    SomeClass(t& _data) : data(_data) {}
    void disp()
    {
        for (auto v : data)
            std::cout << *v << ", ";
        std::cout << std::endl;
    }        
};

int
main(int argc, char* argv[]) {
    int A = 1;
    int B = 2;
    t v = {&A, &B};

    SomeClass f(v);
    f.disp();

    A = 456;
    f.disp();
    return 0;
}

Вам нужен более постоянный вектор, чтобы ваш пример работал.

Класс, как вы указали, это нормально, но он должен документировать себя, чтобы хранить переданную ссылку, поэтому аргумент REQUIRE, который на протяжении всей жизни любит экземпляр SomeClass.

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

Чтобы исправить, создайте вектор в отдельной строке и передайте его в SomeClass ctor.

В настоящее время вы держите ссылку на временное в своем классе, и разыменование его вызывает неопределенное поведение. Вам нужно изменить typedef на вектор (remove &) и добавить вместо него в конструктор: SomeClass(t& _data), чтобы сохранить фактический объект в вашем классе и принять аргумент конструктора в качестве ссылки

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