poo - static em c++




O que significa a palavra-chave explícita? (8)

A palavra-chave explicit acompanha

  • um construtor da classe X que não pode ser usado para converter implicitamente o primeiro (qualquer um) parâmetro para o tipo X

C ++ [class.conv.ctor]

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

2) Um construtor explícito constrói objetos como construtores não explícitos, mas o faz somente onde a sintaxe de inicialização direta (8.5) ou onde os conjuntos (5.2.9, 5.4) são explicitamente usados. Um construtor padrão pode ser um construtor explícito; tal construtor será usado para executar inicialização padrão ou inicialização de valor (8.5).

  • ou uma função de conversão que é considerada apenas para inicialização direta e conversão explícita.

C ++ [class.conv.fct]

2) Uma função de conversão pode ser explícita (7.1.2), caso em que é considerada apenas como uma conversão definida pelo usuário para inicialização direta (8.5). Caso contrário, as conversões definidas pelo usuário não estão restritas para uso em atribuições e inicializações.

visão global

Funções e construtores explícitos de conversão só podem ser usados ​​para conversões explícitas (inicialização direta ou operação de conversão explícita), enquanto construtores não explícitos e funções de conversão podem ser usados ​​para conversões implícitas e explícitas.

/*
                                 explicit conversion          implicit conversion

 explicit constructor                    yes                          no

 constructor                             yes                          yes

 explicit conversion function            yes                          no

 conversion function                     yes                          yes

*/

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

Vejamos uma pequena configuraçã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 sobre o construtor:

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 objeto:

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

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 objeto:

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 que usar funções de conversão explicit ou construtores?

Construtores de conversão e funções de conversão não explícitas podem apresentar ambiguidade.

Considere uma estrutura V , convertível para int , uma estrutura U implicitamente construtível a partir de V e uma função f 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ígua se passar um objeto do tipo V

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

O compilador não sabe se deve 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 o construtor de U ou a função de conversão de V fosse explicit , não haveria ambigüidade, pois somente a conversão não explícita seria considerada. Se ambos forem explícitos, a chamada para f usando um objeto do tipo V teria que ser feita usando uma conversão explícita ou uma operação de conversão.

Construtores de conversão e funções de conversão não explícitas podem levar a um comportamento inesperado.

Considere uma função imprimindo algum vetor:

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

Se o construtor de tamanho do vetor não fosse explícito, seria possível chamar a função assim:

print_intvector(3);

O que alguém esperaria de tal ligação? Uma linha contendo 3 ou três linhas contendo 0 ? (Onde o segundo é o que acontece.)

O uso da palavra-chave explícita em uma interface de classe impõe que o usuário da interface seja explícito sobre uma conversão desejada.

Como Bjarne Stroustrup coloca (em "A Linguagem de Programação em C ++", 4a Ed., 35.2.1, pp. 1011) sobre a questão por que a std::duration não pode ser implicitamente construída a partir de um número simples:

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

O que significa a palavra-chave explicit em C ++?


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


Esta resposta é sobre a criação de objetos com / sem um construtor explícito, uma vez que não é abordado nas 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 da classe Foo podem ser criados de 2 maneiras:

Foo bar1(10);

Foo bar2 = 20;

Dependendo da implementação, a segunda maneira de instanciar a classe Foo pode ser confusa, ou não o que o programador pretendia. Prefixar a palavra-chave explicit ao construtor geraria um erro do compilador em Foo bar2 = 20; .

Em geral, é uma boa prática declarar construtores de argumento único como explicit , a menos que sua implementação proíba isso 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

Ambos podem ser usados ​​como construtores de argumento único. Então você pode querer fazer isso também explicit .

Um exemplo de quando você não deseja tornar explicitamente seu construtor de argumento único é se você está criando um functor (veja a estrutura 'add_x' declarada this resposta). Nesse caso, criar um objeto como add_x add30 = 30; provavelmente faria sentido.

Here está um bom artigo sobre construtores explícitos.


Isso já foi discutido (o que é construtor explícito ). Mas devo dizer que faltam as descrições detalhadas encontradas aqui.

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

Outra boa prática para as aulas é tornar a construção e a atribuição de cópias privadas (também desativadas), a menos que você realmente precise implementá-las. Isso evita cópias eventuais de ponteiros ao usar os métodos que o C ++ criará para você por padrão. Uma outra maneira de fazer isso é derivada de boost :: noncopyable.


O compilador tem permissão para 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 que podem ser chamados com um único parâmetro para converter de um tipo para outro, a fim de obter o tipo correto para um parâmetro.

Aqui está uma classe de exemplo 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 é onde a função DoBar é chamada.

int main ()
{
  DoBar (42);
}

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

O compilador tem permissão para fazer isso uma vez para cada parâmetro.

Prefixar a palavra-chave explicit ao construtor impede que o compilador use esse construtor para conversões implícitas. Adicioná-lo à classe acima criará um erro de compilador na chamada de função DoBar (42) . Agora é necessário solicitar conversão explicitamente com DoBar (Foo (42))

O motivo pelo qual você pode querer fazer isso é evitar construções acidentais que possam ocultar bugs. Exemplo criado:

  • Você tem uma classe MyString(int size) com um construtor que constrói uma string com o tamanho especificado. Você tem uma função print(const MyString&) , e você chama print(3) (quando você realmente pretendia chamar print("3") ). Você espera que ele imprima "3", mas imprime uma seqüência vazia de comprimento 3.

Os construtores acrescentam conversão implícita. Para suprimir essa 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 essa palavra-chave here Com essa especificação, você pode usar o operador em termos de conversões explícitas e inicialização direta do objeto.

PS Ao usar transformações definidas por USER (via construtores e operador de conversão de tipo), é permitido apenas um nível de conversões implícitas usadas. Mas você pode combinar essas conversões com outras conversões de idioma

  • up ranks integrais (char para int, float para double);
  • conversões padrão (int para double);
  • converter ponteiros de objetos para a classe base e para anular *;

Suponha que você tenha uma classe String :

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 caractere 'x' será implicitamente convertido em int e, em seguida, o construtor String(int) será chamado. Mas isso não é o que o usuário pode ter pretendido. Então, 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
};

Construtores de conversão explícitos (somente C ++)

O especificador de função explícito controla as conversões implícitas indesejadas. Ele só pode ser usado em declarações de construtores dentro de uma declaração de classe. Por exemplo, exceto pelo construtor padrão, os construtores da classe a seguir são construtores de conversão.

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 seriam ilegais.

Por exemplo, se você declarar a classe como:

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

Você só pode atribuir valores que correspondam aos valores do tipo de classe.

Por exemplo, as seguintes declaraçõ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);




explicit-constructor