#cpp #cpp11 #конструктор #конструктор_копирования
Надо ли (и если да, то в каких случаях) указывать конструкторы копирования и перемещения как explicit? Видел такое в коде и не совсем понял. Где ненужное неявное преобразование, которое explicit помогает нам избежать?
Ответы
Ответ 1
Ответ на этот вопрос прост: 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, то перестанут работать примеры, где указано явное его использование. То же самое с конструктором копирования. Ответ 2
Ключевое слово explict позволит избежать случайного преобразования типов, которое может привести к неявным ошибкам. Например: У вас есть класс с конструктором MyString(int size), который создает строку размером size. Также есть метод print(const MyString&), при вызове print(3) ты можешь ожидать вызов print("3"), а на самом деле получишь пустую строку из 3 символов. Хороший и развернутый ответ можно найти тут.Ответ 3
Если у Вас будут в дальнейшем определены или явно удалены операторы копирования и перемещения, то в этом нет никакой обходимости.
Комментариев нет:
Отправить комментарий