O que faz a média palavra-chave explícita?
-
02-07-2019 - |
Pergunta
O que faz a explicit
chave média em C ++?
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çãoprint(const MyString&)
, e você chamarprint(3)
(quando você realmente pretendiaprint("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 int
s em Buffer
s. 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 *;