it-swarm-pt.tech

Qual é a lógica por trás da palavra-chave "using" em C ++?

Qual é a lógica por trás da palavra-chave "using" em C++?

Ele é usado em diferentes situações e eu estou tentando descobrir se todos eles têm algo em comum e há uma razão pela qual a palavra-chave "using" é usada como tal.

using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class
94
user3111311

Em C++ 11, a palavra-chave using quando usada para type alias é idêntica a typedef.

7.1.3.2

Um typedef-name também pode ser introduzido por uma declaração de alias. O identificador após a palavra-chave using se torna um typedef-name e o opcional attribute-specifier-seq após o identificador pertence a esse typedef-name. Ele tem a mesma semântica como se fosse introduzido pelo especificador typedef. Em particular, não define um novo tipo e não deve aparecer no ID do tipo.

Bjarne Stroustrup fornece um exemplo prático:

typedef void (*PFD)(double);    // C style
using PF = void (*)(double);    // using plus C-style type
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

Pré-C++ 11, a palavra-chave using pode trazer funções de membro para o escopo. No C++ 11, você pode fazer isso agora para os construtores (outro exemplo do Bjarne Stroustrup):

class Derived : public Base { 
public: 
    using Base::f;    // lift Base's f into Derived's scope -- works in C++98
    void f(char);     // provide a new f 
    void f(int);      // prefer this f to Base::f(int) 

    using Base::Base; // lift Base constructors Derived's scope -- C++11 only
    Derived(char);    // provide a new constructor 
    Derived(int);     // prefer this constructor to Base::Base(int) 
    // ...
}; 

Ben Voight fornece uma boa razão por trás da lógica de não introduzir uma nova palavra-chave ou nova sintaxe. O padrão quer evitar quebrar o código antigo o máximo possível. É por isso que, nos documentos da proposta, você verá seções como Impact on the Standard, Design decisions e como elas podem afetar o código antigo. Há situações em que uma proposta parece ser uma boa ideia, mas pode não ter tração porque seria muito difícil de implementar, confusa demais ou contradizia o código antigo.


Aqui está um artigo antigo de 2003 n1449 . A lógica parece estar relacionada aos modelos. Aviso: pode haver erros devido à cópia do PDF.

Primeiro, vamos considerar um exemplo de brinquedo:

template <typename T>
class MyAlloc {/*...*/};

template <typename T, class A>
class MyVector {/*...*/};

template <typename T>

struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage

O problema fundamental desse idioma, e o principal motivo motivador dessa proposta, é que o idioma faz com que os parâmetros do modelo apareçam em um contexto não dedutível. Ou seja, não será possível chamar a função foo abaixo sem especificar explicitamente os argumentos do modelo.

template <typename T> void foo (Vec<T>::type&);

Então, a sintaxe é um pouco feia. Preferimos evitar o ::type aninhado. Preferiríamos algo como o seguinte:

template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage

Note que nós especificamente evitamos o termo “typedef template” e intruímos a nova sintaxe envolvendo o par “using” e “=” para ajudar a evitar confusão: não estamos definindo nenhum tipo aqui, estamos introduzindo um sinônimo (ie alias) para uma abstração de um tipo de identificação (ou seja, expressão de tipo) envolvendo parâmetros de modelo. Se os parâmetros do modelo forem usados ​​em contextos dedutíveis na expressão de tipo, sempre que o alias do modelo for usado para formar um id de modelo, os valores dos parâmetros de modelo correspondentes poderão ser deduzidos - mais a seguir. Em qualquer caso, agora é possível escrever funções genéricas que operam em Vec<T> no contexto deduzível, e a sintaxe também é melhorada. Por exemplo, poderíamos reescrever foo como:

template <typename T> void foo (Vec<T>&);

Ressaltamos aqui que uma das principais razões para propor aliases de modelo foi para que a dedução de argumentos e a chamada para foo(p) sejam bem-sucedidas.


O documento de acompanhamento n1489 explica por que using em vez de usar typedef:

Foi sugerido para (re) usar a palavra-chave typedef - como feito no artigo [4] - para introduzir aliases de modelo:

template<class T> 
    typedef std::vector<T, MyAllocator<T> > Vec;

Essa notação tem a vantagem de usar uma palavra-chave já conhecida para introduzir um alias de tipo. No entanto, também exibe várias desvantagens, entre as quais a confusão de usar uma palavra-chave conhecida por introduzir um alias para um nome de tipo em um contexto em que o alias não designa um tipo, mas um modelo; Vec não é um alias para um tipo e não deve ser usado para um typedef-name. O nome Vec é um nome para a família std::vector< [bullet] , MyAllocator< [bullet] > > - onde o marcador é um espaço reservado para um nome de tipo. Consequentemente, não propomos a sintaxe “typedef”. Por outro lado a sentença

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

pode ser lido/interpretado como: a partir de agora, usarei Vec<T> como sinônimo de std::vector<T, MyAllocator<T> >. Com essa leitura, a nova sintaxe do aliasing parece razoavelmente lógica.

Eu acho que a distinção importante é feita aqui, alias es em vez de tipo s. Outra citação do mesmo documento:

Uma declaração de alias é uma declaração e não uma definição. Uma declaração de alias introduz um nome em uma região declarativa como um alias para o tipo designado pelo lado direito da declaração. O núcleo desta proposta diz respeito a aliases de nomes de tipos, mas a notação pode obviamente ser generalizada para fornecer grafias alternativas de alias de namespace ou conjunto de nomenclatura de funções sobrecarregadas (veja 2.3 para uma discussão mais aprofundada). [Minha observação: Essa seção discute como essa sintaxe pode parecer e explica por que ela não faz parte da proposta.] Pode-se notar que a declaração de alias de produção gramatical é aceitável em qualquer lugar em que uma declaração typedef ou uma definição de alias de namespace seja aceitável.

Resumo, para o papel de using:

  • aliases de modelo (ou typedefs de modelo, o primeiro é preferencialmente do mesmo modo)
  • aliases de namespace (ou seja, namespace PO = boost::program_options e using PO = ... equivalente)
  • o documento diz A typedef declaration can be viewed as a special case of non-template alias-declaration. É uma mudança estética e é considerada idêntica neste caso.
  • trazendo algo para o escopo (por exemplo, namespace std no escopo global), funções de membro, herdeiros de construtores

Isto não podes ser usado para:

int i;
using r = i; // compile-error

Em vez disso:

using r = decltype(i);

Nomeando um conjunto de sobrecargas.

// bring cos into scope
using std::cos;

// invalid syntax
using std::cos(double);

// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);
80
user1508519