Здесь следует рассмотреть две потенциальные опасности копирования:
Опасность копирования 1: конструкция снаружи getData()
В первой строке main()
, где вы прокомментировали копию всей структуры данных - как отметили комментаторы, структура фактически не будет скопирована из-за оптимизации именованного возвращаемого значения или сокращенно NRVO. Вы можете прочитать об этом в этом хорошем сообщении в блоге, сделанном несколько лет назад:
Fluent{C++}: оптимизация возвращаемых значений
В двух словах: компилятор упорядочивает это так, что data
внутри функции getData
, когда она вызывается из main()
, на самом деле является псевдонимом data
в main.
Опасность копирования 2: data
и data2
Вторая проблема с копированием связана с setA()
и setB()
. Здесь вы должны быть более активными, поскольку у вас есть две живые, действительные структуры в одной и той же функции — data
и data2
внутри getData()
. В самом деле, если Data
и DataFrom
просто большие структуры, то вам придется много копировать из data2
в data
так, как вы написали свой код.
На помощь приходит семантика
Однако, если ваш DataFrom
содержит ссылку на какое-то выделенное хранилище, скажем, std::vector<int> a
вместо int[10000] a
- вы можете переместить из своего DataFrom
вместо копирования из него - имея getData()
с подписью static Data getData(DataFrom&& data2)
. Подробнее о переезде читайте здесь:
Что такое семантика перемещения?
В моем примере это будет означать, что теперь вы будете использовать необработанный буфер data2.a
для вашего data
, не копируя содержимое этого буфера куда-либо еще. Но это будет означать, что вы больше не сможете использовать data2
после этого, так как его поле a
было удалено, перемещено из.
... или просто поленитесь.
Вместо подхода, основанного на движении, вы можете попробовать что-то другое. Предположим, вы определили что-то вроде этого:
class Data {
protected:
DataFrom& source_;
public:
int& a() { return source_.a; }
int& b() { return source_.b; }
public:
Data(DataFrom& source) : source_(source) { }
Data(Data& other) : source_(other.source) { }
// copy constructor?
// assignment operators?
};
Теперь Data
не простая структура; это скорее фасад для DataFrom
(и, возможно, некоторых других полей и методов). Это немного менее удобно, но преимущество в том, что теперь вы создаете Data
, просто ссылаясь на DataFrom
и не копируя ничего другого. При доступе может потребоваться разыменование указателя.
Другие примечания:
Ваш DataHandler
определен как класс, но похоже, что он служит просто пространством имен. Вы никогда не создаете экземпляры обработчиков данных. Подумайте о том, чтобы прочитать:
Почему и как использовать пространства имен в C++?
Мои предложения не связаны с C++17. Семантика перемещения была введена в C++11, и если вы выберете ленивый подход, он будет работать даже в C++98.
person
einpoklum
schedule
14.08.2020
= delete
копировать построение и назначение и разрешать семантику перемещения только в том случае, если вы боитесь копий. - person François Andrieux   schedule 14.08.2020