Питання Що означає явне ключове слово?


Що таке explicit ключове слово означає в C ++?


2371
2017-09-23 13:58


походження


Я просто хочу вказати на кого-небудь нового, що приходить з цим, починаючи з C + + 11, explicit може застосовуватися не просто до конструкторів. Наразі це дійсно застосовується також до операторів конверсії. Скажімо, у вас є клас BigInt з оператором конвертації до int і явний оператор перетворення на std::string з якоїсь причини. Ви зможете сказати int i = myBigInt;, але вам доведеться кидати чітко (використовуючи static_cast, бажано), щоб сказати std::string s = myBigInt;. - chris
Не можу явні також послатися на присвоєння? (тобто int x(5);) - Eitan Myron
theunixshell.blogspot.com/2013/01/explicit-keyword-in-c.html - Vijay
@chris Ідея явного неявного перетворення є абсурдною. Забудьте про це! - curiousguy
@curiousguy Там немає такої речі, як явне неявне перетворення. - chris


Відповіді:


Компілятор може робити одне неявне перетворення, щоб визначити параметри для функції. Що це означає, що компілятор може використовувати конструктори, які можна викликати з a єдиний параметр для перетворення з одного типу в інший, щоб отримати правильний тип для параметра.

Ось приклад класу з конструктором, який можна використовувати для неявних перетворень:

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;
};

Ось проста функція, яка виконує a 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.

2755
2017-09-23 14:09



приємно написати, можливо, ви хочете згадати декількох аргументів, з параметрами за умовчанням може також діяти як single arg ctor, наприклад Object (const char * name = NULL, int otype = 0). - maccullt
Я думаю, що слід також згадати, що слід спочатку розглянути можливість створення одноразових конструкторів аргументів (більш-менш автоматично) і вилучення явного ключового слова лише тоді, коли потрібне неявне перетворення за дизайном. Я думаю, що конструктори повинні бути явними за умовчанням за допомогою "неявного" ключового слова, щоб вони могли працювати як неявні конверсії. Але це не так, як воно і є. - Michael Burr
@thecoshman: Ви не оголошувати a параметр  explicit - ви оголошуєте a конструктор  explicit. Але так: ваші параметри типу Foo повинні бути побудовані expliciteЛи, вони не будуть мовчазно будуватися, просто підключивши їх параметри конструктора до функції. - Christian Severin
Просто FYI, що, коли ви називаєте "print (3)" у своєму прикладі, функція повинна бути "print (const MyString &"). "Констант" є обов'язковим тут, оскільки 3 перетворюється на тимчасовий об'єкт "MyString", і ви не можете прив'язувати тимчасове посилання до посилання, якщо він не "const" (ще один в довгій списку C ++ gotchas) - Larry
За повнотою, я додаю, що крім перетворення параметрів явний ключове слово тут також забороняє використовувати форму присвоєння копії ctor (наприклад, Foo myFoo = 42;) і вимагати явних форм Foo myFoo = Foo (42); або Foo myFoo (42); - Arbalest


Припустимо, у вас є клас 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
};

964
2017-09-23 16:37



І варто зазначити, що будуть створені нові узагальнені правила ініціалізації C ++ 0x String s = {0}; погано сформований, а не намагається називати іншого конструктора нульовим покажчиком, як String s = 0; зробив би. - Johannes Schaub - litb
Незважаючи на те, що це старе питання, варто звернути увагу на декілька речей (або, якщо хтось встановив мене прямо). Зробивши форму int або обидва ctors "явним", ви все одно матимете ту же помилку, якщо ви використовували String mystring('x')коли ти мав на увазі String mystring("x") чи не так? Крім того, з коментаря вище я бачу поліпшену поведінку String s = {0} над String s = 0 завдяки тому, що формування int форма "ctor" явно виражене ". Але, крім того, що ви знаєте перевагу керуючих, як ви знаєте намір (наприклад, як визначити помилку) цього String s{0} ? - Arbalest
Чому String mystring = 'x'; перетворюється в int? - InQusitive
@InQusitive: 'x'розглядається як ціле число, тому що char Тип даних - це всього лише 1-байтове ціле число. - DavidRR
Проблема з вашим прикладом полягає в тому, що вона працює тільки з копіювання ініціалізації (використовуючи =), але не з пряма ініціалізація (без використання =): компілятор все одно буде викликати String(int) конструктор, не створюючи помилку, якщо ви пишете String mystring('x');, як зазначив @Arbalest. The explicit Ключове слово призначене для запобігання неявних перетворень, що відбуваються при прямому ініціалізації та вирішенні функцій. Кращим рішенням для вашого прикладу може бути проста завантаження конструктора: String(char c);. - Maggyero


У C ++ конструктор, що має лише один необхідний параметр, вважається неявною функцією перетворення. Він перетворює тип параметра на тип класу. Чи це добре, чи ні, залежить від семантики конструктора.

Наприклад, якщо у вас є рядка з конструктором String(const char* s), це, мабуть, саме те, що ви хочете. Ви можете пройти a const char* до функції очікує a String, і компілятор автоматично будує тимчасовий String об'єкт для вас

З іншого боку, якщо у вас є клас буфера, конструктор якого Buffer(int size) бере розмір буфера в байтах, ви, мабуть, не хочете, щоб компілятор спокійно поворухнувся intS в Bufferс Щоб запобігти цьому, ви оголошуєте конструктор з explicit ключове слово:

class Buffer { explicit Buffer(int size); ... }

Цей шлях,

void useBuffer(Buffer& buf);
useBuffer(4);

стає помилкою часу компіляції. Якщо ви хочете пройти тимчасовий Buffer об'єкт, ви повинні робити це явно:

useBuffer(Buffer(4));

Таким чином, якщо ваш однопараметровий конструктор перетворить параметр на об'єкт вашого класу, ви, мабуть, не хочете використовувати explicit ключове слово Але якщо у вас є конструктор, який просто буває прийняти один параметр, ви повинні оголосити його як explicitщоб компілятор не дивував вас несподіваними переходами.


130
2017-10-08 14:43



useBuffer очікує на оцінку його аргументації useBuffer(Buffer(4)) ні через це не спрацює. Зміна його, щоб взяти a const Buffer& або Buffer&& або просто Buffer зробить це роботою. - pqnet


Ця відповідь стосується створення об'єкта з / без явного конструктора, оскільки він не розглядається в інших відповідях.

Розглянемо наступний клас без явного конструктора:

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; мабуть, матиме сенс.

Ось тут це хороша характеристика для явних конструкторів.


34
2017-11-21 02:36





The explicit Ключове слово перетворює конструктор перетворення в конструктор без перетворення. Внаслідок цього код схильний до помилок.


31
2017-07-10 23:48



пряма відповідь, якщо ви знаєте c ++, якщо ви не можете перейти до відповіді cjm ... - n611x007


Ключове слово explicit супроводжує теж

  • конструктор класу X, який не може бути використаний для неявного перетворення першого (тільки будь-якого) параметра в тип X

C ++ [class.conv.ctor]

1) Конструктор, оголошений без специфікації функції, явно визначає перетворення з типів його параметрів у тип його класу. Такий конструктор називається конвертованим конструктором.

2) Явний конструктор конструює об'єкти точно так само, як і невиразні конструктори, але робить це тільки в тому випадку, якщо явно використовується синтаксис прямого ініціалізації (8.5) або відступ (5.2.9, 5.4). Конструктор за замовчуванням може бути явним конструктором; такий конструктор буде використовуватися для виконання ініціалізації за замовчуванням або ініціалізації вартості   (8.5).

  • або функція перетворення, яка розглядається тільки для прямої ініціалізації та явного перетворення.

C ++ [class.conv.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 не може бути неявно побудований з простого числа:

Якщо ви знаєте, що ви маєте на увазі, будь ясним про це.


31
2018-05-14 09:28





The explicitключове слово може бути використане для виконання конструктора, який потрібно викликати явно.

class C{
public:
    explicit C(void) = default;
};

int main(void){
    C c();
    return 0;
}

в 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.


25
2017-10-01 22:00



C c(); в першому прикладі не означає, що ви думаєте, це означає: це декларація функції, названої c що не приймає параметрів і повертає екземпляр C. - 6502
explicit operator bool() також є версією безпечного bool C ++ 11, і може бути використана неявно в умовах перевірок (і тільки в умовах перевірок, наскільки я знаю). У другому прикладі, ця рядок також буде дійсною в main(): if (c) { std::cout << "'c' is valid." << std:: endl; }. Крім цього, він не може бути використаний без явного відтворення. - Justin Time
"конструктор, який слід назвати явним чином" немає - curiousguy
@JustinTime Це незаплямована, зламана версія безпечного болю. Вся ідея явного неявного перетворення є абсурдною. - curiousguy
@ Curiousguy Правда Здається, це трохи схоже на хребет, націлений на більшу легкість запам'ятовування (швидше за все, в надії на те, що перекладається на часто використовуване), ніж у наступній англійській логіці, і розроблений таким чином, щоб він не був абсолютно несумісним з попередніми реалізаціями безпечного балу (так що ви менше можливо, щось зламається, якщо ви його заміните). ІМО, принаймні. - Justin Time


Це вже було обговорено (що є явним конструктором) Але треба сказати, що тут не вистачає докладних описів.

Крім того, це завжди хороша практика кодування, щоб зробити ваш одного конструктора аргументів (включаючи ті, що мають значення за замовчуванням для arg2, arg3, ...), як вже зазначалося. Як завжди, з C ++: якщо ви цього не зробите - ви хочете, щоб ви це зробили ...

Ще одна хороша практика для класів полягає в тому, щоб зробити конструкцію копії та присвоєння приватним (а.к.а. відключити його), якщо ви дійсно не потребуєте його реалізації. Це дозволяє уникнути наявності кінцевих копій вказівників при використанні методів, які C ++ буде створювати для вас за замовчуванням. Інший спосіб зробити це виходить з boost :: noncopyable.


17
2017-08-20 12:45



Цей пост написано в 2009 році. Сьогодні ви не декларуєте їх як приватні, а швидше кажуть = delete. - v010dya
@fmuecke посилання мертве, питання було видалено - David Cabrera


Довідник 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
}

15
2017-12-20 12:19



Так! Вивчення прикладу та опису ключового слова на cppreference - це найкращий спосіб зрозуміти explicit Я думаю. - dekuShrub
explicit operator bool() проти if це особливий випадок. Неможливо відтворити його за допомогою визначеного користувача Bool, explicit operator Bool() і функція називається If. - curiousguy