Co oznacza słowo kluczowe explicit?

Co oznacza słowo kluczowe explicit W C++?

Author: Ronny Brendel, 2008-09-23

11 answers

Kompilator może wykonać jedną domyślną konwersję w celu przekształcenia parametrów w funkcję. Oznacza to, że kompilator może używać konstruktorów wywołanych z pojedynczym parametrem do konwersji z jednego typu na inny w celu uzyskania odpowiedniego typu dla parametru.

Oto przykładowa klasa z konstruktorem, która może być użyta do niejawnych konwersji:

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

Oto prosta funkcja, która przyjmuje Foo obiekt:

void DoBar (Foo foo)
{
  int i = foo.GetFoo ();
}

A oto gdzie wywołana jest funkcja DoBar.

int main ()
{
  DoBar (42);
}

Argument nie jest obiektem Foo, ale int. Jednak istnieje konstruktor Foo, który przyjmuje int, więc ten konstruktor może być użyty do konwersji parametru na właściwy typ.

Kompilator może to zrobić raz dla każdego parametru.

Prefiks słowa kluczowego explicit do konstruktora uniemożliwia kompilatorowi użycie tego konstruktora do niejawnych konwersji. Dodanie go do powyższej klasy spowoduje tworzenie błędu kompilatora przy wywołaniu funkcji DoBar (42). Obecnie konieczne jest jawne wywołanie konwersji za pomocą DoBar (Foo (42))

Powodem, dla którego warto to zrobić, jest uniknięcie przypadkowej konstrukcji, która może ukryć błędy. Zmyślony przykład:

  • masz klasę MyString(int size) z konstruktorem, który konstruuje łańcuch o podanym rozmiarze. Masz funkcję print(const MyString&) i wywołujesz print(3) (kiedy faktycznie zamierzałeś wywołać print("3")). Oczekujesz, że wydrukuje "3", ale to wypisuje pusty ciąg o długości 3.
 2823
Author: Skizz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-24 18:46:20

Załóżmy, że masz klasę String:

class String {
public:
    String(int n); // allocate n bytes to the String object
    String(const char *p); // initializes object with char *p
};

Teraz, jeśli spróbujesz:

String mystring = 'x';

Znak 'x' zostanie domyślnie przekonwertowany na int, a następnie zostanie wywołany konstruktor String(int). Ale to nie jest to, co użytkownik może mieć zamierzony. Aby zapobiec takim warunkom, zdefiniujemy konstruktor jako explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};
 982
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 09:28:46

W C++ konstruktor z tylko jednym wymaganym parametrem jest uważany za ukrytą funkcję konwersji. Konwertuje parametr type na typ klasy. To, czy jest to dobra rzecz, czy nie, zależy od semantyki konstruktora.

Na przykład, jeśli masz klasę string z konstruktorem String(const char* s), to prawdopodobnie właśnie tego chcesz. Możesz przekazać const char* do funkcji oczekującej String, a kompilator automatycznie zbuduje tymczasowy obiekt String dla ty.

Z drugiej strony, jeśli masz klasę bufora, której konstruktor Buffer(int size) przyjmuje rozmiar bufora w bajtach, prawdopodobnie nie chcesz, aby kompilator po cichu zamieniał int s w Buffer s. aby temu zapobiec, deklarujesz konstruktor za pomocą słowa kluczowego explicit:

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

W ten sposób,

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

Staje się błędem w czasie kompilacji. Jeśli chcesz przekazać tymczasowy obiekt Buffer, musisz to zrobić jawnie:

useBuffer(Buffer(4));

W podsumowaniu, jeśli twój konstruktor jednoparametrowy konwertuje parametr na obiekt swojej klasy, prawdopodobnie nie chcesz używać słowa kluczowego explicit. Ale jeśli masz konstruktor, który po prostu pobiera pojedynczy parametr, powinieneś zadeklarować go jako explicit, aby zapobiec zaskoczeniu kompilatora nieoczekiwanymi konwersjami.

 136
Author: cjm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-23 16:37:12

Ta odpowiedź dotyczy tworzenia obiektu z / bez jawnego konstruktora, ponieważ nie jest ona uwzględniona w innych odpowiedziach.

Rozważmy następującą klasę bez wyraźnego konstruktora:

class Foo
{
public:
    Foo(int x) : m_x(x)
    {
    }

private:
    int m_x;
};

Obiekty klasy Foo mogą być tworzone na dwa sposoby:

Foo bar1(10);

Foo bar2 = 20;

W zależności od implementacji, drugi sposób tworzenia instancji klasy Foo może być mylący lub nie taki, jaki zamierzał programista. Prefiks słowa kluczowego explicit do konstruktora spowodowałby błąd kompilatora w Foo bar2 = 20;.

Jest Zazwyczaj dobrą praktyką deklarowanie konstruktorów jednowątkowych jako explicit, chyba że implementacja wyraźnie tego zabrania.

Zauważ również, że konstruktory z

  • domyślne argumenty dla wszystkich parametrów, lub
  • domyślne argumenty dla drugiego parametru

Mogą być używane jako konstruktory jednoargumentowe. Więc możesz chcieć je również explicit.

Przykład kiedy celowo Nie jeśli tworzysz funktor (spójrz na strukturę 'add_x' zadeklarowaną w ta odpowiedź), to chcesz, aby twój jednowarstwowy konstruktor był jawny. W takim przypadku utworzenie obiektu jako add_x add30 = 30; prawdopodobnie miałoby sens.

Tutaj {[35] } jest dobry zapis na jawnych konstruktorach.

 36
Author: Gautam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:32

Słowo kluczowe explicit tworzy konstruktor konwersji na konstruktor nie-konwertujący. W rezultacie kod jest mniej podatny na błędy.

 33
Author: SankararaoMajji,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-14 16:40:06

Słowo kluczowe explicit towarzyszy albo

  • konstruktor klasy X, który nie może być użyty do domyślnej konwersji pierwszego (jedynego) parametru na typ X

C++ [class.conv.ctor]

1) Konstruktor zadeklarowany bez specyfikacji funkcji jawnie określa konwersję z typów jego parametrów do typu jego klasy. Taki konstruktor nazywa się konstruktorem konwertującym.

2) An jawny Konstruktor konstruuje obiekty tak samo jak jawne konstruktory, ale robi to tylko wtedy, gdy jawnie używana jest składnia bezpośredniej inicjalizacji (8.5) lub gdy jawnie używane są casts (5.2.9, 5.4). Konstruktor domyślny może być konstruktorem jawnym; taki konstruktor będzie używany do wykonywania domyślnej inicjalizacji lub valueinitialization (8.5).

  • lub funkcja konwersji, która jest brana pod uwagę tylko dla bezpośredniej inicjalizacji i jawnej nawrócenie.

C++ [class.conv.fct]

2) funkcja konwersji może być jawna (7.1.2), w którym to przypadku jest traktowana tylko jako konwersja zdefiniowana przez użytkownika dla bezpośredniej inicjalizacji (8.5). W przeciwnym razie konwersje zdefiniowane przez użytkownika nie są ograniczone do wykorzystania w przydziałach i inicjalizacji.

Przegląd

Jawne funkcje konwersji i konstruktory mogą być używane tylko do jawnych konwersji (bezpośrednia inicjalizacja lub explicit Cast operation), podczas gdy nie-jawne konstruktory i funkcje konwersji mogą być używane zarówno do konwersji implicit, jak i explicit.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Przykład użycia struktur X, Y, Z i funkcji foo, bar, baz:

Przyjrzyjmy się małej konfiguracji struktur i funkcji, aby zobaczyć różnicę między konwersjami explicit i nie-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) { }

Przykłady dotyczące konstruktora:

Konwersja argumentu funkcji:

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

Obiekt inicjalizacja:

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

Przykłady dotyczące funkcji konwersji:

X x1{ 0 };
Y y1{ 0 };

Konwersja argumentu funkcji:

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

Inicjalizacja obiektu:

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

Po co używać explicit funkcji konwersji lub konstruktorów?

Konstruktory konwersji i niewyrażalne funkcje konwersji mogą wprowadzać niejednoznaczność.

Rozważmy strukturę V, zamienioną na int, strukturę U niejawnie zbudowaną z V oraz funkcję f przeciążone odpowiednio dla U i bool.

struct V {
  operator bool() const { return true; }
};

struct U { U(V) { } };

void f(U) { }
void f(bool) {  }

Wywołanie do f jest niejednoznaczne, jeśli przekazuje obiekt typu V.

V x;
f(x);  // error: call of overloaded 'f(V&)' is ambiguous

Kompilator nie wie, aby używać konstruktora U lub funkcji konwersji do konwersji V obiektu do typu do przekazania f.

Jeśli konstruktor U lub funkcja konwersji V będzie explicit, nie będzie niejednoznaczności, ponieważ tylko konwersja Jawna będzie przemyślane. Jeśli oba są jawne, wywołanie f przy użyciu obiektu typu V musiałoby zostać wykonane przy użyciu jawnej operacji konwersji lub cast.

Konstruktory konwersji i niewyrażalne funkcje konwersji mogą prowadzić do nieoczekiwanego zachowania.

Rozważmy funkcję drukującą jakiś wektor:

void print_intvector(std::vector<int> const &v) { for (int x : v) std::cout << x << '\n'; }

Jeśli konstruktor wielkości wektora nie byłby jawny, możliwe byłoby wywołanie funkcji w następujący sposób:

print_intvector(3);

Co by spodziewasz się po takim telefonie? Jedna linia zawierająca 3 czy trzy linie zawierające 0? (Gdzie drugim jest to, co się dzieje.)

Użycie jawnego słowa kluczowego w interfejsie klasy wymusza, aby użytkownik interfejsu był jawny co do pożądanej konwersji.

Jak to ujął Bjarne Stroustrup (w "języku programowania C++", 4th Ed. 1011) na pytanie, dlaczego std::duration nie może być pośrednio skonstruowany z liczby zwykłej:

If you know what you bądź szczery.

 33
Author: Pixelchemist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-12 11:29:40

Słowo kluczowe explicit - może być użyte do wymuszenia wywołania konstruktora jawnie .

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

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

Słowo kluczowe explicit-przed konstruktorem C(void) mówi kompilatorowi, że dozwolone jest tylko jawne wywołanie tego konstruktora.

Słowo kluczowe explicit - może być również używane w operatorach typu cast zdefiniowanych przez Użytkownika:

class C{
public:
    explicit inline operator bool(void) const{
        return true;
    }
};

int main(void){
    C c;
    bool b = static_cast<bool>(c);
    return 0;
}

Tutaj, explicit - keyword wymusza tylko wyraźne rzuty, aby były ważne, więc bool b = c; będzie nieważnym rzutem w tym przypadku. W takich sytuacjach explicit - Słowo kluczowe może pomóc programiście uniknąć ukrytych, niezamierzonych odlewów. Użycie to zostało znormalizowane w C++11 .

 25
Author: Helixirr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-13 09:08:04

To już zostało omówione (co jest jawnym konstruktorem ). Muszę jednak powiedzieć, że brakuje w nim szczegółowych opisów.

Poza tym, zawsze dobrą praktyką kodowania jest tworzenie konstruktorów jednego argumentu (w tym tych z domyślnymi wartościami dla arg2, arg3,...), jak już stwierdzono. Jak zawsze z C++: jeśli nie-będziesz żałował...

Inną dobrą praktyką dla klas jest uczynienie budowy kopii i przypisania prywatnym (A.k. a. wyłączyć go) chyba, że naprawdę musisz to wdrożyć. Pozwala to uniknąć ewentualnych kopii wskaźników podczas korzystania z metod, które C++ utworzy dla Ciebie domyślnie. Innym sposobem na to jest boost:: noncopyable.

 17
Author: fmuecke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:28

Referencja Cpp jest zawsze pomocna!!! Szczegóły dotyczące jawnego specyfikatora można znaleźć tutaj . Być może trzeba również przyjrzeć się niejawnym konwersjom i copy-initialization .

Quick look

Jawny specyfikator określa, że konstruktor lub funkcja Konwersji (od C++11) nie pozwala na niejawne konwersje lub kopiowanie-inicjalizację.

Przykład w następujący sposób:

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
Author: selfboot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-20 12:45:16

Explicit conversion constructors (C++ only)

Jawny specyfikator funkcji kontroluje niechciany niejawny Typ konwersje. Może być stosowany tylko w deklaracjach konstruktorów w ramach deklaracji klasy. Na przykład, z wyjątkiem domyślnego konstruktor, konstruktory w następującej klasie są konwersją konstruktorów.

class A
{
public:
    A();
    A(int);
    A(const char*, int = 0);
};

Następujące deklaracje są legalne:

A c = 1;
A d = "Venditti";

Pierwsza deklaracja jest równoważna A c = A( 1 );.

Jeśli zadeklarujesz konstruktor klasy jako explicit, poprzednie deklaracje będą nielegalne.

Na przykład, jeśli zadeklarujesz klasę jako:

class A
{
public:
    explicit A();
    explicit A(int);
    explicit A(const char*, int = 0);
};

Możesz przypisać tylko wartości, które pasują do wartości typu klasy.

Na przykład, następujące stwierdzenia są legalne:

  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);
 10
Author: coding_ninza,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-20 09:32:49

Konstruktory dołączają do niejawną konwersję. Aby stłumić tę konwersję implicit, wymagane jest zadeklarowanie konstruktora z parametrem explicit.

W C++11 można również określić "typ operatora ()" za pomocą takiego słowa kluczowego http://en.cppreference.com/w/cpp/language/explicit z taką specyfikacją można używać operatora w kategoriach jawnych konwersji i bezpośredniej inicjalizacji obiektu.

P. S. przy użyciu przekształceń zdefiniowanych przez użytkownika (poprzez konstruktory i typ operator konwersji) dozwolony jest tylko jeden poziom konwersji niejawnych. Ale możesz połączyć te konwersje z innymi konwersjami językowymi

  • up integral ranks (char to int, float to double);
  • standart conversions (int to double);
  • konwersja wskaźników obiektów do klasy bazowej i do void*;
 4
Author: bruziuz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-23 09:26:52