Страницы

Поиск по вопросам

вторник, 12 марта 2019 г.

“explicit” для конструкторов копирования и перемещения

Надо ли (и если да, то в каких случаях) указывать конструкторы копирования и перемещения как explicit? Видел такое в коде и не совсем понял. Где ненужное неявное преобразование, которое explicit помогает нам избежать?


Ответ

Ответ на этот вопрос прост: explicit нужен там, где он нужен, а где не нужен — там не нужен. Есть распространённый совет, что все конструкторы, что принимают один аргумент должны быть помечены explicit, во избежание случайного создания объектов класса там, где их создание не очевидно. К примеру, есть у нас функция void someFun(const std::shared_ptr&) и мы, совершенно случайно, используем её так:
int* array = new int[55]; someFun(array); delete[] array;
Если бы конструктор std::shared_ptr не был explicit, то код бы успешно скомпилировался и на третьей строчке мы бы получили неопределённое поведение. Но т.к. конструктор std::shared_ptr помечен как explicit мы не получим такую ситуацию случайно. Таким образом, explicit призван исключить случайные ошибки.
Что касается explicit конструктора копирования: ни разу не видел такого и не могу придумать, зачем такой может быть нужен. Если мы хотим запретить копирование, то нужно удалить его вообще, если не хотим, то зачем вставлять палки в колёса разрешая лишь узкий набор синтаксических вариантов, используя которые можно будет скопировать объект?
В качестве примера можно привести следующий код:
#include #include
using namespace std;
class MeClass { public: MeClass(size_t idx, const string& tag = "") : m_Idx{idx}, m_Tag{tag} { } MeClass(const MeClass& rhs): m_Idx{rhs.m_Idx}, m_Tag{rhs.m_Tag} { } private: size_t m_Idx; string m_Tag; };
MeClass explicitReturn() { // Явный вызов конструктора // Неявный вызов конструктора копирования // move нужен исключительно для исключения C++17 RVO return move(MeClass{2, "meTag"}); }
MeClass implicitReturn() { // Неявный вызов конструктора // Неявный вызов конструктора копирования (до C++17) return {3, "meaTag"}; }
void funForFun(const MeClass& me) {
}
int main() { // Явный вызов конструктора MeClass me1{1}; // Неявный вызов конструктора MeClass me2 = 2; // Неявный вызов конструктора копирования auto me3 = me1; // Явный вызов конструктора копирования auto me4{me1}; // Неявный вызов конструктора funForFun(2); funForFun({3}); // Явный вызов конструктора funForFun(me1); };
Если пометить в коде конструктор как explicit, то перестанут работать примеры, где указано явное его использование. То же самое с конструктором копирования.

Комментариев нет:

Отправить комментарий