Главная » Хабрахабр » explicit в деталях

explicit в деталях

Если спросить C++-программиста о значении ключевого слова explicit, большинство ответит, что это ключевое слово ставится перед объявлением конструктора с одним параметром (или с большим числом параметров, но когда все параметры, начиная со второго, имеют значения по умолчанию) и предотвращает неявное преобразование типов при инициализации.

class Simple {
public: Simple(int a) : a_(a)
private: int a_;
}; class SimpleExplicit {
public: explicit SimpleExplicit(int a) : a_(a) {}
private: int a_;
}; template <typename S>
void someFunc(const S& s) {
} int main(int, char**) { Simple s3 = 11; // SimpleExplicit se3 = 11; - COMPILE ERROR SimpleExplicit se3 = SimpleExplicit(11); someFunc<Simple>(11); // someFunc<SimpleExplicit>(11); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit(11)); return 0;
}

В старом добром C++03 сценарии применения ключевого слова на этом заканчивались, однако, начиная с C++11, область применения explicit расширилась: теперь оно имеет смысл не только в конструкторах с одним параметром, и даже не только в конструкторах.

Я не буду здесь подробно рассказывать про универсальную инициализацию, на эту тему есть множество подробных статей, их несложно найти по ключевым словам. В 2011 году в Стандарт добавили универсальную инициализацию (uniform initialization), которая должна навести порядок в зоопарке способов инициализации объектов, доставшемся C++ в наследство от языка C. агрегатной инициализации (aggregate initialization), унаследованной ещё со времён C. В двух словах: объекты предлагается инициализировать при помощи фигурных скобок, по сути это расширение т.н.

С появлением универсальной инициализации explicit обрёл смысл для конструкторов с 0,2,3 и более параметров:

class Simple {
public: Simple() : a_(0), b_(0) {} Simple(int a) : a_(a), b_(0) {} Simple(int a, int b) : a_(a), b_(b) {}
private: int a_, b_;
}; class SimpleExplicit {
public: explicit SimpleExplicit() : a_(0), b_(0) {} explicit SimpleExplicit(int a) : a_(a), b_(0) {} explicit SimpleExplicit(int a, int b) : a_(a), b_(b) {}
private: int a_, b_;
}; template <typename S>
void someFunc(const S& s) {
} int main(int, char**) { Simple s4 = {}; someFunc<Simple>({}); // SimpleExplicit se4 = {}; - COMPILE ERROR SimpleExplicit se4 = SimpleExplicit{}; // someFunc<SimpleExplicit>({}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{}); Simple s5 = {11}; someFunc<Simple>({11}); // SimpleExplicit se5 = {11}; - COMPILE ERROR SimpleExplicit se5 = SimpleExplicit{11}; // someFunc<SimpleExplicit>({11}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{11}); Simple s6 = {11, 22}; someFunc<Simple>({11, 22}); // SimpleExplicit se6 = {11, 22}; - COMPILE ERROR SimpleExplicit se6 = SimpleExplicit{11, 22}; // someFunc<SimpleExplicit>({11, 22}); - COMPILE ERROR someFunc<SimpleExplicit>(SimpleExplicit{11, 22}); return 0;
}

Помимо этого, начиная с C++11 ключевое слово explicit может также применяться к операторам преобразования типа, также запрещая их неявный вызов:

class Simple {
public: Simple() {} operator bool() const { return true; }
}; class SimpleExplicit {
public: explicit SimpleExplicit() {} explicit operator bool() const { return true; }
}; int main(int, char**) { Simple s7{}; bool b7 = s7; SimpleExplicit se7{}; // bool be7 = se7; - COMPILE ERROR bool be7 = static_cast<bool>(se7); return 0;
}

В заключение хочется порекомендовать использовать универсальную инициализацию в любом новом коде на C++, а также явно объявлять конструкторы explicit всегда, кроме случаев, когда неявное преобразование семантически оправдано.


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

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Профессиональная IoT-конференция InoThings++ — что было и что будет

Привет, Хабр! Практически ровно год назад — в конце января 2018-го — мы попробовали провести первую профессиональную конференцию для разработчиков устройств, систем и проектов «Интернета вещей» InoThings++ 2018. Помимо того, что она была первой для нас — если не считать ...

[Перевод] Изучаем Python: модуль argparse

Если вы занимаетесь обработкой и анализом данных с использованием Python, то вам, рано или поздно, придётся выйти за пределы Jupyter Notebook, преобразовав свой код в скрипты, которые можно запускать средствами командной строки. Здесь вам и пригодится модуль argparse. Для новичков, ...