Pergunta

O que faz a explicit chave média em C ++?

Foi útil?

Solução

O compilador é permitido fazer uma conversão implícita para resolver os parâmetros para uma função. O que isto significa é que o compilador pode usar construtores resgatáveis ??com um parâmetro único para converter de um tipo para outro, a fim de obter o direito tipo para um parâmetro.

Aqui está um exemplo de classe com um construtor que pode ser usado para conversões implícitas:

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

Aqui está uma função simples que leva um objeto Foo:

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

e é aqui que a função DoBar é chamado.

int main ()
{
  DoBar (42);
}

O argumento não é um objeto Foo, mas um int. No entanto, existe um construtor para Foo que leva um int assim que este construtor pode ser usado para converter o parâmetro para o tipo correto.

O compilador é permitido fazer isso uma vez para cada parâmetro.

Se anteceder a palavra-chave explicit para o construtor impede o compilador de usar esse construtor para conversões implícitas. Adicioná-lo à classe acima irá criar um erro do compilador no DoBar (42) chamada de função. É agora necessário para chamar para a conversão explicitamente com DoBar (Foo (42))

A razão que você pode querer fazer isso é evitar a construção acidental que pode esconder erros. exemplo artificial:

  • Você tem uma classe MyString(int size) com um construtor que constrói uma string de dimensão dada. Você tem uma função print(const MyString&), e você chamar print(3) (quando você realmente pretendia print("3") chamada). Você espera que ele para imprimir "3", mas ele imprime uma string vazia de comprimento 3 em vez disso.

Outras dicas

Suponha que você tem uma String classe:

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

Agora, se você tentar:

String mystring = 'x';

O 'x' personagem vai ser implicitamente convertido para int e, em seguida, o construtor String(int) será chamado. Mas, isso não é o que o usuário pode ter pretendido. Assim, para evitar tais condições, vamos definir o construtor como explicit:

class String {
public:
    explicit String (int n); //allocate n bytes
    String(const char *p); // initialize sobject with string p
};

C ++, um construtor com apenas um parâmetro desejado é considerada uma função de conversão implícita. Ele converte o tipo de parâmetro para o tipo de classe. Se isto é uma coisa boa ou não, depende a semântica do construtor.

Por exemplo, se você tem uma classe string com String(const char* s) construtor, que é provavelmente exatamente o que você quer. Você pode passar um const char* para uma função esperando um String, eo compilador irá construir automaticamente um objeto String temporária para você.

Por outro lado, se você tem uma classe tampão cuja Buffer(int size) construtor recebe o tamanho do buffer em bytes, você provavelmente não quer que o compilador silenciosamente virar ints em Buffers. Para evitar isso, você declarar o construtor com a palavra chave explicit:

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

Dessa forma,

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

torna-se um erro de tempo de compilação. Se você quiser passar um objeto Buffer temporária, você tem que fazê-lo explicitamente:

useBuffer(Buffer(4));

Em resumo, se o seu construtor de parâmetro único converte o parâmetro em um objeto de sua classe, você provavelmente não quer usar a palavra-chave explicit. Mas se você tem um construtor que simplesmente acontece a tomar um único parâmetro, você deve declará-lo como explicit para evitar o compilador de surpreender-lhe com conversões inesperadas.

Esta resposta é sobre a criação de objetos com / sem um construtor explícito, uma vez que não é coberto em outras respostas.

Considere a seguinte classe sem um construtor explícito:

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

private:
    int m_x;
};

Objetos de classe Foo pode ser criado em 2 maneiras:

Foo bar1(10);

Foo bar2 = 20;

Dependendo da aplicação, a segunda forma de instanciar classe Foo pode ser confuso, ou não o que o programador pretendia. Prefixar a palavra-chave explicit para o construtor iria gerar um erro do compilador em Foo bar2 = 20;.

É normalmente boa prática para declarar construtores de argumento único como explicit, a menos que a sua implementação proíbe especificamente.

Note também que construtores com

  • argumentos padrão para todos os parâmetros, ou
  • argumentos padrão para o segundo parâmetro em diante

tanto pode ser usado como construtores de argumento único. Então você pode querer fazer estes também explicit.

Um exemplo quando você deliberadamente não quer fazer o seu construtor de argumento único explícita é, se você está criando um functor (olhar para o 'add_x' struct declarado em esta resposta ). Em tal caso, a criação de um objeto como add_x add30 = 30; provavelmente faz sentido.

Aqui é um bom contra gravação -se em construtores explícitos.

A palavra-chave explicit faz um construtor de conversão para o construtor não-conversão. Como resultado, o código é menos propenso a erros.

A palavra-chave acompanha explicit quer

  • um construtor de classe X que não pode ser usado para implicitamente converter o (qualquer único) parâmetro para digitar X
  • primeira

C ++ [class.conv.ctor]

1) Um construtor declarou sem especifica explícitas função especificador uma conversão dos tipos de seus parâmetros para o tipo de sua classe. Tal construtor é chamado um construtor de conversão.

2) um construções construtor explícito objetos apenas como construtores não-explícitas, mas fá-lo apenas quando a sintaxe de inicialização direta (8,5) ou em que moldes (5.2.9, 5.4) são usados ??de forma explícita. Um construtor padrão pode ser um construtor explícito; como um construtor será usado para executar default-inicialização ou valueinitialization (8,5).

  • ou uma função de conversão que só é considerado para a inicialização direta e conversão explícita.

C ++ [class.conv.fct]

2) função de conversão de A pode ser explícito (7.1.2), caso em que ele só é considerada como uma conversão definido pelo utilizador para a inicialização-directo (8,5). Caso contrário, as conversões definidos pelo utilizador não estão restritos ao uso em atribuições e inicializações.

Visão geral

funções de conversão explícitas e construtores só pode ser utilizado para conversões explícitas (inicialização directa ou operação de conversão explícita) enquanto construtores não explicitados e as funções de conversão podem ser utilizados para implícito, bem como as conversões explícitos.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

Exemplo usando X, Y, Z e funções foo, bar, baz:

Vamos olhar uma pequena instalação de estruturas e funções para ver a diferença entre conversões explicit e não 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) { }

Exemplos referentes construtor:

A conversão de um argumento de função:

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

inicialização do objecto:

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

Exemplos sobre funções de conversão:

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

A conversão de um argumento de função:

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

inicialização do objecto:

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

Por funções de conversão de uso explicit ou construtores?

construtores de conversão e funções de conversão não-explícitas podem introduzir ambiguidade.

Considere um V estrutura, convertível em int, um U estrutura implicitamente construtivel de V e um f função sobrecarregada para U e bool respectivamente.

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

struct U { U(V) { } };

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

Uma chamada para f é ambíguo se passando um objeto do tipo V.

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

O compilador não sabe wether usar o construtor de U ou a função de conversão para converter o objeto V em um tipo para passar para f.

Se um construtor de U ou a função de conversão de V seria explicit, não haveria nenhuma ambiguidade uma vez que apenas a conversão não explícita seria considerado. Se ambos forem explícita a chamada para f usando um objeto do tipo V teria que ser feito usando uma operação de conversão ou de conversão explícita.

construtores de conversão e funções de conversão não-explícitas pode levar a um comportamento inesperado.

Considere uma função de imprimir algum vetor:

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

Se o tamanho-construtor do vector não seria explícita seria possível chamar a função como esta:

print_intvector(3);

O que esperar de tal chamado um? Um contendo 3 linha ou três linhas contendo 0? (Onde o segundo é o que acontece.)

Usando a palavra-chave explícita em uma interface de classe impõe o usuário da interface para ser explícito sobre a conversão desejada.

Como Bjarne Stroustrup coloca (em "The C ++ Programming Language", 4ª Ed, 35.2.1, pp 1011..) sobre a questão porque std::duration não pode ser construída implicitamente a partir de um número simples:

Se você sabe o que você quer dizer, ser explícito sobre isso.

conversão explícita construtores (C ++ apenas)

O especificador de função explícita controla implícita de tipo indesejado conversões. Ele só pode ser usado em declarações de construtores dentro de uma declaração de classe. Por exemplo, exceto para o padrão construtor, os construtores da seguinte classe são conversão construtores.

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

As seguintes declarações são legais:

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

A primeira declaração é equivalente a A c = A( 1 );.

Se você declarar o construtor da classe como explicit, as declarações anteriores seria ilegal.

Por exemplo, se você declarar a classe como:

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

Você pode apenas valores atribuir que correspondem aos valores do tipo de classe.

Por exemplo, as seguintes afirmações são legais:

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

O explicit-chave pode ser usada para impor um construtor para ser chamado explicitamente .

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

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

o explicit-chave na frente do C(void) construtor diz ao compilador que chamada só explícita para esse construtor é permitido.

O explicit-chave também pode ser usado em operadores de conversão tipo definido pelo usuário:

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

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

Aqui, aplica explicit-chave apenas conversões explícitas para ser válido, então bool b = c; seria um elenco inválido neste caso. Em situações como essas explicit-chave pode ajudar programador para evitar implícitas, elencos não intencionais. Esse uso foi padronizado em C ++ 11 .

Cpp Referência é sempre útil !!! Detalhes sobre especificador explícita pode ser encontrada aqui . Você pode precisar de olhar para conversões implícitas e copiar-inicialização também.

olhada rápida

Os especifica especificador explícitas que uma função de construtor ou conversão (desde C ++ 11) não permite conversões implícitas ou copiar-inicialização.

Exemplo como se segue:

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
}

Esta já foi discutido ( que está explícito construtor ). Mas devo dizer, que ela não tem as descrições detalhadas encontrada aqui.

Além disso, é sempre uma boa prática de codificação para fazer seus construtores um argumento (incluindo aqueles com valores padrão para arg2, arg3, ...) como já foi dito. Como sempre com C ++: se você não fizer isso - você deseja que você fez ...

Outra boa prática para as aulas é fazer com que a construção cópia e atribuição privada (desativar A.K.A.-lo) a menos que você realmente precisa para implementá-lo. Isso evita ter eventuais cópias de ponteiros quando utilizar os métodos que C ++ irá criar para você por padrão. Uma outra maneira de fazer isso é derive de boost :: noncopyable.

Construtores anexar conversão implícita. Para suprimir esta conversão implícita é necessário declarar um construtor com um parâmetro explícito.

Em C ++ 11 você também pode especificar um "tipo de operador ()" com tal palavra-chave http://en.cppreference.com/w/cpp/language/explicit Com tal especificação você pode usar o operador em termos de conversões explícitas, e inicialização direta do objeto.

P.S. Quando usando transformações definida por UTILIZADOR (via construtores e operador de conversão tipo) é permitido apenas um nível de conversões implícitas utilizados. Mas você pode combinar esta conversões com outras conversões de linguagem

  • -se fileiras integrais (char para int, float dobrar);
  • conversões do standart (int para double);
  • ponteiros converter de objetos a classe base e para void *;
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top