Что означает ключевое слово explicit в C++?
Не может явным образом также относиться к присваиванию? (например, int x(5);)
@chris Идея явного неявного преобразования абсурдна. Держитесь подальше от этого!
@curiousguy, Явного неявного преобразования нет.
@chris Существует явное ключевое слово, которое можно использовать при объявлении неявного преобразования.
@curiousguy, это по своей сути неявное преобразование. Размещение explicit декларирует явное преобразование в тип. Никакой скрытности в процессе.
@chris Явное преобразование - это плохо определенная концепция.
@curiousguy: Что ты имеешь в виду? Все преобразования должны быть неявными? Выпустить всевозможные молчаливые забавные ошибки из-за случайных двусмысленностей? (См., Например, раздел «Проблема безопасного bool» в этот C++ исх. страница или open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2333.html для (гораздо) более подробной информации о том, почему «Явное преобразование - плохо определенная концепция». Это непродуманное утверждение.)
@Sz. Я имею в виду, что явное преобразование - это не вещь; это мусор. Также «safe bool» - смехотворное «доказательство» полезности идеи «явного оператора», поскольку это даже не приложение этой идеи, а другой набор правил, что означает Единственное практическое использование «явного оператора» в SL является специальным и не применимо к UDT.
@ Крис, ты имел в виду это: std::string s = static_cast<std::string>(myBigInt)? Если возможно, не могли бы вы подробнее рассказать о своем первом комментарии? Заранее большое спасибо!
@Milan, да именно так. Если вам нужна дополнительная информация, этот ответ записывает ее более формально. Обратите внимание, что bool особенный в этом отношении. Эти ответы и поиск «явных операторов преобразования» приведут вас к большему количеству рецензий об этой функции и будут лучше подходить, чем цепочка комментариев.





Компилятору разрешено сделать одно неявное преобразование для преобразования параметров в функцию. Это означает, что компилятор может использовать конструкторы, вызываемые с помощью единственный параметр, для преобразования из одного типа в другой, чтобы получить правильный тип для параметра.
Вот пример класса с конструктором, который можно использовать для неявных преобразований:
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);
}
Аргументом является не объект Foo, а int. Однако существует конструктор для Foo, который принимает int, поэтому этот конструктор можно использовать для преобразования параметра в правильный тип.
Компилятору разрешено сделать это один раз для каждого параметра.
Добавление перед конструктором ключевого слова explicit не позволяет компилятору использовать этот конструктор для неявных преобразований. Добавление его в вышеуказанный класс вызовет ошибку компилятора при вызове функции DoBar (42). Теперь необходимо явно вызвать преобразование с помощью DoBar (Foo (42)).
Причина, по которой вы можете захотеть это сделать, - избежать случайной конструкции, которая может скрыть ошибки. Надуманный пример:
MyString(int size) с конструктором, который создает строку заданного размера. У вас есть функция print(const MyString&), и вы вызываете print(3) (когда вы фактически намеревались вызвать print("3")). Вы ожидаете, что он напечатает «3», но вместо этого он напечатает пустую строку длиной 3.Хорошая запись, вы можете упомянуть акторы с несколькими аргументами с параметрами по умолчанию, которые также могут действовать как один аргумент ctor, например, Object (const char * name = NULL, into type = 0).
Я думаю, также следует упомянуть, что следует подумать о том, чтобы сделать конструкторы с одним аргументом явными изначально (более или менее автоматически) и удалить явное ключевое слово только тогда, когда требуется неявное преобразование по дизайну. Я думаю, что конструкторы по умолчанию должны быть явными с ключевым словом implicit, чтобы они могли работать как неявные преобразования. Но это не так.
@thecoshman: Вы не объявляете параметрexplicit - вы объявляете конструкторexplicit. Но да: ваши параметры типа Foo должны быть сконструированы explicitely, они не будут сконструированы молча, просто подключив параметры их конструктора в функцию.
Просто к сведению, что при вызове «print (3)» в вашем примере функция должна быть «print (const MyString &»). Здесь "const" является обязательным, потому что 3 преобразуется во временный объект "MyString", и вы не можете привязать временный объект к ссылке, если он не "const" (еще один в длинном списке ошибок C++)
Для полноты картины я добавляю, что в дополнение к преобразованию параметров ключевое слово явный здесь также предотвратит использование формы присваивания копии ctor (например, Foo myFoo = 42;) и потребует явных форм Foo myFoo = Foo (42) ; или Foo myFoo (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++ 0x сделают String s = {0}; некорректным, вместо того, чтобы пытаться вызвать другой конструктор с нулевым указателем, как это сделал бы String s = 0;.
Несмотря на то, что это старый вопрос, кажется, стоит указать на несколько моментов (или попросить кого-нибудь меня поправить). Сделав форму int или оба ctors "явными", вы все равно будете иметь ту же ошибку, если бы вы использовали String mystring('x'), имея в виду String mystring("x"), не так ли? Кроме того, из комментария выше я вижу улучшенное поведение String s = {0} по сравнению с String s = 0 благодаря тому, что форма int для ctor стала «явной». Но, помимо знания приоритета ctors, как узнать намерение (то есть, как обнаружить ошибку) этого String s{0}?
Почему String mystring = 'x'; преобразуется в int?
@InQusitive: 'x' обрабатывается как целое число, потому что Тип данных char - это просто 1-байтовое целое число.
Проблема с вашим примером заключается в том, что он работает только с копировать инициализацию (с использованием =), но не с прямая инициализация (без использования =): компилятор по-прежнему будет вызывать конструктор String(int) без генерации ошибки, если вы напишете String mystring('x');, как указал @Arbalest. Ключевое слово explicit предназначено для предотвращения неявных преобразований, которые происходят при прямой инициализации и разрешении функций. Лучшим решением для вашего примера была бы простая перегрузка конструктора: String(char c);.
В C++ конструктор только с одним обязательным параметром считается функцией неявного преобразования. Он преобразует тип параметра в тип класса. Хорошо это или нет, зависит от семантики конструктора.
Например, если у вас есть строковый класс с конструктором 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, чтобы компилятор не удивлял вас неожиданными преобразованиями.
useBuffer ожидает lvalue для своего аргумента, useBuffer(Buffer(4)) не будет работать из-за этого. Поменяв его на const Buffer&, Buffer&& или просто Buffer, он заработает.
Всегда рекомендуется создавать конструкторы с одним аргументом (включая те, которые имеют значения по умолчанию для arg2, arg3, ...), как уже было сказано.
Как всегда с C++: если нет - вы пожалеете ...
Еще одна хорошая практика для классов - сделать создание копий и присваивание частными (или отключить их), если вам действительно не нужно это реализовывать. Это позволяет избежать возможных копий указателей при использовании методов, которые C++ создаст для вас по умолчанию. Другой способ сделать это - унаследовать от boost::noncopyable.
Этот пост написан в 2009 году. Сегодня вы не объявляете их частными, а говорите = delete.
Ключевое слово explicit превращает конструктор преобразования в конструктор без преобразования. В результате код менее подвержен ошибкам.
прямой ответ, если вы знаете С ++, если нет, вы можете перейти к ответу cjm ...
Ключевое слово explicit может использоваться для принудительного вызова конструктора явно.
class C{
public:
explicit C(void) = default;
};
int main(void){
C c();
return 0;
}
ключевое слово explicit перед конструктором C(void) сообщает компилятору, что разрешен только явный вызов этого конструктора.
Ключевое слово 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.
C c(); в первом примере означает не то, что вы думаете: это объявление функции с именем c, которая не принимает параметров и возвращает экземпляр C.
explicit operator bool() также является версией безопасного bool для C++ 11 и может использоваться неявно при проверках условий (и Только при проверках условий, насколько мне известно). Во втором примере эта строка также действительна в main(): if (c) { std::cout << "'c' is valid." << std:: endl; }. Однако, помимо этого, его нельзя использовать без явного приведения типов.
"конструктор, который будет вызываться явно" нет
@JustinTime Это глупая, сломанная версия безопасного bool. Сама идея явного неявного преобразования абсурдна.
@curiousguy Верно. Это немного похоже на кладж, направленный больше на то, чтобы его легко запомнить (вероятно, в надежде на то, что это будет часто использоваться), чем на следование английской логике, и спроектированный так, чтобы не быть полностью несовместимым с предыдущими безопасными реализациями bool (так что вы меньше может что-то сломать, если вы его замените). ИМО, по крайней мере.
Этот ответ касается создания объекта с / без явного конструктора, поскольку он не рассматривается в других ответах.
Рассмотрим следующий класс без явного конструктора:
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;.
Здесь - хорошая статья о явных конструкторах.
Конструкторы добавляют неявное преобразование. Чтобы подавить это неявное преобразование, необходимо объявить конструктор с явным параметром.
В C++ 11 вы также можете указать «тип оператора ()» с таким ключевым словом http://en.cppreference.com/w/cpp/language/explicit. С такой спецификацией вы можете использовать оператор в терминах явных преобразований и прямой инициализации объекта.
P.S. При использовании преобразований, определенных ПОЛЬЗОВАТЕЛЕМ (через конструкторы и оператор преобразования типов), разрешается использовать только один уровень неявных преобразований. Но вы можете комбинировать эти преобразования с преобразованиями других языков.
explicit сопровождает либоC++ [class.conv.ctor]
1) A constructor declared without the function-specifier explicit specifies a conversion from the types of its parameters to the type of its class. Such a constructor is called a converting constructor.
2) An explicit constructor constructs objects just like non-explicit constructors, but does so only where the direct-initialization syntax (8.5) or where casts (5.2.9, 5.4) are explicitly used. A default constructor may be an explicit constructor; such a constructor will be used to perform default-initialization or valueinitialization (8.5).
C++ [class.conv.fct]
2) A conversion function may be explicit (7.1.2), in which case it is only considered as a user-defined conversion for direct-initialization (8.5). Otherwise, user-defined conversions are not restricted to use in assignments and initializations.
Явные функции преобразования и конструкторы могут использоваться только для явных преобразований (прямая инициализация или явная операция приведения), в то время как неявные конструкторы и функции преобразования могут использоваться как для неявных, так и для явных преобразований.
/*
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? (Где второй - то, что происходит.)
Как выразился Бьярн Страуструп (в "The C++ Programming Language", 4th Ed., 35.2.1, pp. 1011) на вопрос, почему std::duration не может быть неявно сконструирован из простого числа:
If you know what you mean, be explicit about it.
Ссылка Cpp всегда полезна !!! Подробности о явном спецификаторе можно найти в здесь. Возможно, вам также понадобится посмотреть на неявные преобразования и копирование-инициализация.
Быстрый взгляд
The explicit specifier specifies that a constructor or conversion function (since C++11) doesn't allow implicit conversions or copy-initialization.
Пример следующим образом:
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
}
Да! Я думаю, что изучение примера и описания ключевого слова в cppreference - лучший способ понять explicit.
explicit operator bool() против if - особый случай. Невозможно воспроизвести его с помощью определенных пользователем Bool, explicit operator Bool() и функции If.
Конструкторы явного преобразования (только C++)
The explicit function specifier controls unwanted implicit type conversions. It can only be used in declarations of constructors within a class declaration. For example, except for the default constructor, the constructors in the following class are conversion constructors.
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);
"присвоить значения" Мы не обсуждаем здесь оператор присваивания.
Это прямая копия с ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rzarg/…
Копирование и вставка ответа из авторитетного источника без каких-либо ссылок по-прежнему приносит этому парню колоссальные 55 баллов. Неплохо для «старания».
reputable source и прямая копия вводят понятие explicit A(), хотя это просто шум (компилятор должен его игнорировать), но не должен присутствовать в ответе с 55 баллами.
авторитетный источник ошибается, когда говорит: «Он [явный] может использоваться только в объявлениях конструкторов ...», поскольку он также может использоваться в объявлении функций преобразования.
Я просто хочу указать на то, что со времен C++ 11
explicitможет применяться не только к конструкторам. Теперь он применим и к операторам преобразования. Допустим, у вас есть классBigIntс оператором преобразования вintи явным оператором преобразования вstd::stringпо какой-либо причине. Вы сможете сказатьint i = myBigInt;, но вам нужно будет явно выполнить приведение (предпочтительно, используяstatic_cast), чтобы сказатьstd::string s = myBigInt;.