Что означает явное ключевое слово?
что значит explicit
ключевое слово означает в C++?
11 ответов:
компилятору разрешено сделать одно неявное преобразование для разрешения параметров в функцию. Это означает, что компилятор может использовать конструкторы, вызываемые с помощью один параметр для преобразования из одного типа в другой для того, чтобы получить правильный тип параметра.
вот пример класса с конструктором, который можно использовать для неявных преобразований:
class Foo { public: // single parameter constructor, can be used as an implicit conversion Foo (int foo) : m_foo (foo) { } int GetFoo () { return m_foo; } private: int m_foo; };
вот простая функция, которая принимает
Foo
объект:void DoBar (Foo foo) { int i = foo.GetFoo (); }
и здесь
DoBar
вызывается функция.int main () { DoBar (42); }
аргумент не
Предположим, у вас есть класс
String
:class String { public: String(int n); // allocate n bytes to the String object String(const char *p); // initializes object with char *p };
теперь, если вы попробуете:
String mystring = 'x';
символ
'x'
будет неявно преобразовано вint
а тоString(int)
конструктор будет вызван. Но, это не то, что пользователь мог бы иметь в виду. Итак, чтобы предотвратить такие условия, определим конструктор какexplicit
:class String { public: explicit String (int n); //allocate n bytes String(const char *p); // initialize sobject with string p };
в C++ конструктор только с одним обязательным параметром считается неявной функцией преобразования. Он преобразует тип параметра в тип класса. Является ли это хорошей вещью или нет, зависит от семантики конструктор.
например, если у вас есть класс string с конструктором
String(const char* s)
, Это наверное именно то, что вы хотите. Вы можете пройтиconst char*
к функции, ожидающейString
, и компилятор автоматически построит временныйString
объект для вас.С другой стороны, если у вас есть буфер класса, конструктор которого
Buffer(int size)
принимает размер буфера в байтах, вы, вероятно, не хотите, чтобы компилятор, чтобы спокойноint
наBuffer
ы. Чтобы предотвратить это, вы объявляете конструктор сexplicit
ключевые слова:class Buffer { explicit Buffer(int size); ... }
таким образом,
void useBuffer(Buffer& buf); useBuffer(4);
будет ошибка времени компиляции. Если вы хотите передать временный
Buffer
объект, вы должны делать так в явном виде:useBuffer(Buffer(4));
таким образом, если ваш однопараметрический конструктор преобразует параметр в объект вашего класса, вы, вероятно, не хотите использовать
explicit
ключевое слово. Но если у вас есть конструктор, который просто принимает один параметр, вы должны объявить его какexplicit
чтобы компилятор не удивил вас неожиданными преобразованиями.
этот ответ касается создания объекта с / без явного конструктора, поскольку он не рассматривается в других ответах.
рассмотрим следующий класс без явного конструктора:
class Foo { public: Foo(int x) : m_x(x) { } private: int m_x; };
объекты класса Foo могут быть созданы двумя способами:
Foo bar1(10); Foo bar2 = 20;
в зависимости от реализации, второй способ создания экземпляра класса Foo может привести к путанице, или не то, что программист намеревался. Приставка
explicit
для сайта конструктор будет генерировать ошибку компилятора вFoo bar2 = 20;
.это обычно рекомендуется объявлять конструкторы с одним аргументом как
explicit
, Если ваша реализация специально не запрещает это.обратите внимание также, что конструкторы с
- аргументов по умолчанию для всех параметров, или
- аргументов по умолчанию для второго параметра
можно использовать как конструкторы с одним аргументом. Так что вы можете сделайте это также
explicit
.пример, когда вы сознательно не хотите сделать свой конструктор с одним аргументом явным, если вы создаете функтор (посмотрите на структуру "add_x", объявленную в этой ответ). В таком случае, создавая объект как
add_x add30 = 30;
вероятно, имеет смысл.здесь это хорошая запись о явных конструкторах.
на
explicit
ключевое слово делает конструктор преобразования в конструктор без преобразования. В результате код менее подвержен ошибкам.
ключевое слово
explicit
сопровождает либо
- конструктор класса X, который не может быть использован для неявного преобразования первого (любого только) параметра в тип X
в C++ [класс.конв.ctor]
1) конструктор, объявленный без спецификатора функции, явно указывает преобразование из типов его параметров в тип его класса. Такой конструктор называется конструктор преобразования.
2) явный конструктор создает объекты так же, как и неявные конструкторы, но делает это только там, где явно используется синтаксис прямой инициализации (8.5) или где явно используются приведения (5.2.9, 5.4). Конструктор по умолчанию может быть явным конструктором; такой конструктор будет использоваться для выполнения инициализации по умолчанию или valueinitialization (8.5).
- или функция преобразования, которая учитывается только для прямая инициализация и явное преобразование.
в C++ [класс.конв.fct]
2) функция преобразования может быть явной (7.1.2), и в этом случае она рассматривается только как пользовательское преобразование для прямой инициализации (8.5). В противном случае пользовательские преобразования не ограничиваются использованием в назначениях и инициализации.
обзор
явные функции преобразования и конструкторы могут использоваться только для явных преобразований (прямая инициализация или явная операция приведения), в то время как неявные конструкторы и функции преобразования могут использоваться как для явных, так и для неявных преобразований.
/* explicit conversion implicit conversion explicit constructor yes no constructor yes yes explicit conversion function yes no conversion function yes yes */
пример использования конструкции
X, Y, Z
и функцииfoo, bar, baz
:давайте посмотрим на небольшую настройку структур и функций, чтобы увидеть разницу между
explicit
и неexplicit
преобразования.struct Z { }; struct X { explicit X(int a); // X can be constructed from int explicitly explicit operator Z (); // X can be converted to Z explicitly }; struct Y{ Y(int a); // int can be implicitly converted to Y operator Z (); // Y can be implicitly converted to Z }; void foo(X x) { } void bar(Y y) { } void baz(Z z) { }
примеры что касается конструктора:
преобразование аргумента функции:
foo(2); // error: no implicit conversion int to X possible foo(X(2)); // OK: direct initialization: explicit conversion foo(static_cast<X>(2)); // OK: explicit conversion bar(2); // OK: implicit conversion via Y(int) bar(Y(2)); // OK: direct initialization bar(static_cast<Y>(2)); // OK: explicit conversion
объект инициализации:
X x2 = 2; // error: no implicit conversion int to X possible X x3(2); // OK: direct initialization X x4 = X(2); // OK: direct initialization X x5 = static_cast<X>(2); // OK: explicit conversion Y y2 = 2; // OK: implicit conversion via Y(int) Y y3(2); // OK: direct initialization Y y4 = Y(2); // OK: direct initialization Y y5 = static_cast<Y>(2); // OK: explicit conversion
примеры относительно функций преобразования:
X x1{ 0 }; Y y1{ 0 };
преобразование аргумента функции:
baz(x1); // error: X not implicitly convertible to Z baz(Z(x1)); // OK: explicit initialization baz(static_cast<Z>(x1)); // OK: explicit conversion baz(y1); // OK: implicit conversion via Y::operator Z() baz(Z(y1)); // OK: direct initialization baz(static_cast<Z>(y1)); // OK: explicit conversion
объект инициализации:
Z z1 = x1; // error: X not implicitly convertible to Z Z z2(x1); // OK: explicit initialization Z z3 = Z(x1); // OK: explicit initialization Z z4 = static_cast<Z>(x1); // OK: explicit conversion Z z1 = y1; // OK: implicit conversion via Y::operator Z() Z z2(y1); // OK: direct initialization Z z3 = Z(y1); // OK: direct initialization Z z4 = static_cast<Z>(y1); // OK: explicit conversion
зачем использовать
explicit
функции преобразования или конструкторы?конструкторы преобразования и неявные функции преобразования могут внести неопределенность.
рассмотрим структуру
V
, конвертируемых вint
, строениеU
подспудно разобравшись сV
иf
перегружены дляU
иbool
соответственно.struct V { operator bool() const { return true; } }; struct U { U(V) { } }; void f(U) { } void f(bool) { }
вызов
f
неоднозначно при передаче объекта типаV
.V x; f(x); // error: call of overloaded 'f(V&)' is ambiguous
компилятор не знает, нужно ли использовать конструктор
U
или функция преобразования для преобразованияV
объект в тип для передачиf
.если конструктор
U
или функция преобразованияV
будетexplicit
, не будет никакой двусмысленности, поскольку будет рассматриваться только неявное преобразование. Если оба являются явными вызовf
используя объект типаV
должно быть сделано с использованием явного преобразования или операции приведения.конструкторы преобразования и неявные функции преобразования может привести к неожиданному поведению.
рассмотрим функцию печати некоторого вектора:
void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }
если размер-конструктор вектора не будет явным, можно было бы вызвать функцию следующим образом:
print_intvector(3);
чего можно было ожидать от такого звонка? Одна строка, содержащая
3
или три строки, содержащие0
? (Где второй - это то, что происходит.)использование явного ключевого слова в интерфейсе класса принудительно пользователь интерфейса должен быть явным о желаемом преобразовании.
как говорит Бьярне Страуструп (в "языке программирования C++", 4-е изд., 35.2.1, стр. 1011) на вопрос, почему
std::duration
не может быть неявно построен из простого числа:если вы знаете, что вы имеете в виду, быть явными об этом.
The
explicit
-ключевое слово может быть использовано для принудительного применения конструктора, который будет вызван явно.class C{ public: explicit C(void) = default; }; int main(void){ C c(); return 0; }
the
explicit
- ключевое слово перед конструкторомC(void)
сообщает компилятору, что разрешен только явный вызов этого конструктора.The
explicit
-ключевое слово также может использоваться в пользовательских операторами приведения типа:class C{ public: explicit inline operator bool(void) const{ return true; } }; int main(void){ C c; bool b = static_cast<bool>(c); return 0; }
здесь
explicit
-ключевое слово применяет только явные приведения, чтобы быть действительным, так чтоbool b = c;
будет недопустимым приведением этот случай. В таких ситуацияхexplicit
-ключевое слово может помочь программисту избежать неявных, непреднамеренных приведений. Это использование было стандартизировано в C++11.
Это уже обсуждалось (что такое явный конструктор). Но я должен сказать, что ему не хватает подробных описаний здесь.
кроме того,это всегда хорошая практика кодирования, чтобы сделать ваши конструкторы одного аргумента (в том числе со значениями по умолчанию для arg2, arg3,...) как уже говорилось. Как всегда с C++: если вы этого не сделаете - вы пожалеете об этом...
еще одна хорошая практика для классов - сделать создание копий и назначение частными (a.k.a. отключите его), если вам действительно не нужно его реализовать. Это позволяет избежать возможных копий указателей при использовании методов, что C++ будет создавать для вас по умолчанию. Другой способ сделать это-получить от boost::noncopyable.
ссылка Cpp всегда полезна!!! Подробности о явном спецификаторе можно найти здесь. Вы можете посмотреть неявные преобразования и копирования-инициализации тоже.
быстрый просмотр
явный спецификатор указывает, что конструктор или функция преобразования (начиная с C++11) не допускает неявных преобразований или инициализации копирования.
пример:
struct A { A(int) { } // converting constructor A(int, int) { } // converting constructor (C++11) operator bool() const { return true; } }; struct B { explicit B(int) { } explicit B(int, int) { } explicit operator bool() const { return true; } }; int main() { A a1 = 1; // OK: copy-initialization selects A::A(int) A a2(2); // OK: direct-initialization selects A::A(int) A a3 {4, 5}; // OK: direct-list-initialization selects A::A(int, int) A a4 = {4, 5}; // OK: copy-list-initialization selects A::A(int, int) A a5 = (A)1; // OK: explicit cast performs static_cast if (a1) cout << "true" << endl; // OK: A::operator bool() bool na1 = a1; // OK: copy-initialization selects A::operator bool() bool na2 = static_cast<bool>(a1); // OK: static_cast performs direct-initialization // B b1 = 1; // error: copy-initialization does not consider B::B(int) B b2(2); // OK: direct-initialization selects B::B(int) B b3 {4, 5}; // OK: direct-list-initialization selects B::B(int, int) // B b4 = {4, 5}; // error: copy-list-initialization does not consider B::B(int,int) B b5 = (B)1; // OK: explicit cast performs static_cast if (b5) cout << "true" << endl; // OK: B::operator bool() // bool nb1 = b2; // error: copy-initialization does not consider B::operator bool() bool nb2 = static_cast<bool>(b2); // OK: static_cast performs direct-initialization }
явные конструкторы преобразования (только C++)
явный спецификатор функции управляет нежелательным неявным типом преобразования. Он может использоваться только в объявлениях конструкторов в объявлении класса. Например, за исключением значения по умолчанию конструктор, конструкторы в следующем классе преобразования проектировщики.
class A { public: A(); A(int); A(const char*, int = 0); };
следующие декларации являются законными:
A c = 1; A d = "Venditti";
первый объявление эквивалентно
A c = A( 1 );
.если вы объявляете конструктор класса как
explicit
предыдущие заявления, будет незаконным.например, если вы объявляете класс, как:
class A { public: explicit A(); explicit A(int); explicit A(const char*, int = 0); };
можно назначать только значения, соответствующие значениям типа класса.
например, следующие утверждения являются законными:
A a1; A a2 = A(1); A a3(1); A a4 = A("Venditti"); A* p = new A(1); A a5 = (A)1; A a6 = static_cast<A>(1);
конструкторы добавляют неявное преобразование. Чтобы подавить это неявное преобразование, необходимо объявить конструктор с параметром explicit.
В C++11 вы также можете указать "тип оператора ()" с таким ключевым словом http://en.cppreference.com/w/cpp/language/explicit с такой спецификацией вы можете использовать оператор в терминах явных преобразований и прямой инициализации объекта.
P. S. Когда с помощью преобразований, определенных пользователем (через конструкторы и оператор преобразования типов) допускается использование только одного уровня неявных преобразований. Но вы можете объединить эти преобразования с другими языковыми преобразованиями
- до интегральных рангов (char до int, float до double);
- стандартные преобразования (int в double);
- преобразование указателей объектов в базовый класс и void*;