Определение члена класса через ссылку, переданную через конструктор

Я работаю над реализацией связанного списка на С++, и мне было интересно узнать время жизни исходных данных. Допустим, вызывающая программа создает новый ListNode со следующим конструктором. Меня беспокоит то, что исходная переменная может сильно варьироваться, выходить за рамки, оставляя неопределенное поведение, ЕСЛИ назначение члена данных не является копией значения.

ListNode<T>::ListNode(const T &value) : data(value) {}

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

РЕДАКТИРОВАТЬ: добавление дополнительной информации для ясности

list_node.hpp

template <typename T>
class ListNode
{

friend class List<T>;

public:
   ListNode<T>(const T &);

private:
   T data;

};

список.hpp

template<typename T>
class List
{
public:
   void insert_at_front(const T &);
private:
   ListNode<T> *get_new_node(const T &);
};

list.cpp

void List<T>::insert_at_front(const T &value)
{
   ListNode<T> *new_node_ptr = get_new_node(value);

   ...
}

ListNode<T> *List<T>::get_new_node(const T &value)
{
   return new ListNode<T>(value);
}

person atomSmasher    schedule 21.10.2016    source источник
comment
Это зависит от типа data.   -  person juanchopanza    schedule 22.10.2016
comment
Если вам нужна копия, вы должны передавать ее по значению, а не по ссылке. Но, конечно, если data является ссылкой, это не поможет.   -  person Mark Ransom    schedule 22.10.2016
comment
Значение опубликовать объявление data.   -  person juanchopanza    schedule 22.10.2016
comment
@MarkRansom Здесь нет причин передавать по значению. Если вы не заботитесь о производительности, это нормально, и вам не нужно вызывать std::move. Если вы заботитесь о производительности, у вас в любом случае должны быть перегрузки const& и &&, которые работают быстрее.   -  person Nir Friedman    schedule 22.10.2016
comment
@Nir Friedman: Когда кто-то точно знает, что нужна копия, то передача по значению, а затем выполнение std::move довольно хорошо с точки зрения производительности и избавляет от необходимости писать несколько перегрузок. Это правда, что из этого можно выжать немного больше производительности, написав эти перегрузки. Но заявление о том, что подход «передача по значению, затем перемещение» означает, что не нужно заботиться о производительности, далеко не соответствует действительности.   -  person AnT    schedule 22.10.2016
comment
@AnT Перегрузки здесь являются тривиальными однострочными вкладышами; если вы заботитесь о производительности, вы просто пишете оба и заканчиваете. Если нет, то нет. Передача по значению также на самом деле работает хуже для lvalue, чем передача по константной ссылке. В любом случае, есть определенные ситуации, когда передача по значению хороша, но это не очень хороший общий совет и не очень хороший совет для новичка.   -  person Nir Friedman    schedule 22.10.2016
comment
Понижение вопроса без объяснения причин чрезвычайно полезно. Может быть, я должен добавить больше контекста?   -  person atomSmasher    schedule 22.10.2016
comment
@Nir Friedman: Это усилия по обслуживанию нескольких объявлений, которые делают написание нескольких перегрузок чем-то вроде рутинной работы, даже если они являются однострочными. Опять же, забота о производительности — не такая уж черно-белая вещь, как вы ее представляете. Я бы даже сказал, что подход с передачей по значению следует применять по умолчанию до тех пор, пока вы не будете уверены, что это станет бременем для производительности. Я не уверен, что вы имеете в виду под хуже для lvalue. Штраф в подходе с передачей по значению всегда составляет всего один дополнительный ход. Переезды обычно очень дешевы. Но да, они требуют времени.   -  person AnT    schedule 22.10.2016
comment
Никогда не написанный код не содержит ошибок.   -  person user4581301    schedule 22.10.2016


Ответы (1)


struct Foo {
    Foo(const Bar& b) : m_b(b) {}

    Bar m_b;    
};

struct Foo2 {
    Foo2(const Bar& b) : m_b(b) {}

    const Bar& m_b;    
};

Как видите, код конструктора одинаков, но эти два класса делают что-то другое. Первый класс сделает копию переданного Bar, поэтому он "безопасен", если переданная локальная переменная выходит за пределы области видимости. Второй класс просто захватывает ссылку. По сути, это похоже на хранение ссылки на переданную переменную. Поэтому, если переданная переменная выходит за пределы области видимости, Foo2 будет иметь висячую ссылку.

person Nir Friedman    schedule 21.10.2016