C++ 中“using”关键字背后的逻辑是什么?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20790932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:18:51  来源:igfitidea点击:

What is the logic behind the "using" keyword in C++?

c++c++11

提问by user3111311

What is the logic behind the "using" keyword in C++?

C++ 中“using”关键字背后的逻辑是什么?

It is used in different situations and I am trying to find if all those have something in common and there is a reason why the "using" keyword is used as such.

它在不同的情况下使用,我试图找出所有这些是否有共同点,以及为什么使用“using”关键字是有原因的。

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

采纳答案by user3111311

In C++11, the usingkeyword when used for type aliasis identical to typedef.

在 C++11 中,using关键字 fortype aliastypedef.

7.1.3.2

7.1.3.2

A typedef-name can also be introduced by an alias-declaration. The identifier following the using keyword becomes a typedef-name and the optional attribute-specifier-seq following the identifier appertains to that typedef-name. It has the same semantics as if it were introduced by the typedef specifier. In particular, it does not define a new type and it shall not appear in the type-id.

typedef-name 也可以通过别名声明引入。using 关键字后面的标识符成为 typedef-name,标识符后面的可选属性说明符序列属于该 typedef-name。它与由 typedef 说明符引入的语义相同。特别是,它没有定义新的类型,也不会出现在 type-id 中。

Bjarne Stroustrup provides a practical example:

Bjarne Stroustrup 提供了一个实际的例子:

typedef void (*PFD)(double);    // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double);    // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

Pre-C++11, the usingkeyword can bring member functions into scope. In C++11, you can now do this for constructors (another Bjarne Stroustrup example):

在 C++11 之前,using关键字可以将成员函数带入作用域。在 C++11 中,您现在可以为构造函数执行此操作(另一个 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 provides a pretty good reason behind the rationale of not introducing a new keyword or new syntax. The standard wants to avoid breaking old code as much as possible. This is why in proposal documents you will see sections like Impact on the Standard, Design decisions, and how they might affect older code. There are situations when a proposal seems like a really good idea but might not have traction because it would be too difficult to implement, too confusing, or would contradict old code.

Ben Voight 为不引入新关键字或新语法的基本原理提供了一个很好的理由。该标准希望尽可能避免破坏旧代码。这就是为什么在建议文件,你会看到喜欢的部分Impact on the StandardDesign decisions以及它们如何影响老年人的代码。在某些情况下,提案看起来确实是个好主意,但可能没有吸引力,因为它太难实施、太混乱或与旧代码相矛盾。



Here is an old paper from 2003 n1449. The rationale seems to be related to templates. Warning: there may be typos due to copying over from PDF.

这是 2003 n1449的旧论文。理由似乎与模板有关。警告:由于从 PDF 复制,可能会有错别字。

First let's consider a toy example:

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

The fundamental problem with this idiom, and the main motivating fact for this proposal, is that the idiom causes the template parameters to appear in non-deducible context. That is, it will not be possible to call the function foo below without explicitly specifying template arguments.

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

So, the syntax is somewhat ugly. We would rather avoid the nested ::typeWe'd prefer something like the following:

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

Note that we specifically avoid the term “typedef template” and intr oduce the new syntax involving the pair “using” and “=” to help avoid confusion: we are not defining any types here, we are introducing a synonym (i.e. alias) for an abstraction of a type-id (i.e. type expression) involving template parameters. If the template parameters are used in deducible contexts in the type expression then whenever the template alias is used to form a template-id, the values of the corresponding template parameters can be deduced – more on this will follow. In any case, it is now possible to write generic functions which operate on Vec<T>in deducible context, and the syntax is improved as well. For example we could rewrite foo as:

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

We underscore here that one of the primary reasons for proposing template aliases was so that argument deduction and the call to foo(p)will succeed.

首先让我们考虑一个玩具示例:

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

这个习语的根本问题,也是这个提议的主要动机,是这个习语导致模板参数出现在不可推导的上下文中。也就是说,如果不明确指定模板参数,就不可能调用下面的函数 foo。

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

所以,语法有点难看。我们宁愿避免嵌套::type我们更喜欢以下内容:

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

请注意,我们特别避免使用术语“typedef 模板”,并引入了涉及“using”和“=”对的新语法以帮助避免混淆:我们在这里没有定义任何类型,我们引入了一个同义词(即别名)涉及模板参数的类型标识(即类型表达式)的抽象。如果模板参数在类型表达式的可推导上下文中使用,那么无论何时使用模板别名来形成模板 ID,都可以推导出相应模板参数的值——更多内容将在后面介绍。在任何情况下,现在都可以编写Vec<T>在可推论上下文中操作的泛型函数,并且语法也得到了改进。例如,我们可以将 foo 重写为:

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

我们在这里强调,提出模板别名的主要原因之一是参数推导和调用foo(p)将成功。



The follow-up paper n1489explains why usinginstead of using typedef:

后续论文n1489解释了为什么using不使用typedef

It has been suggested to (re)use the keyword typedef — as done in the paper [4] — to introduce template aliases:

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

That notation has the advantage of using a keyword already known to introduce a type alias. However, it also displays several disavantages among which the confusion of using a keyword known to introduce an alias for a type-name in a context where the alias does not designate a type, but a template; Vecis not an alias for a type, and should not be taken for a typedef-name. The name Vecis a name for the family std::vector< [bullet] , MyAllocator< [bullet] > >– where the bullet is a placeholder for a type-name. Consequently we do not propose the “typedef” syntax. On the other hand the sentence

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

can be read/interpreted as: from now on, I'll be using Vec<T>as a synonym for std::vector<T, MyAllocator<T> >. With that reading, the new syntax for aliasing seems reasonably logical.

有人建议(重新)使用关键字 typedef——如论文 [4] 中所做的那样——来引入模板别名:

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

这种表示法的优点是使用已知的关键字来引入类型别名。但是,它也显示了一些缺点,其中在别名不是指定类型而是模板的上下文中使用已知关键字为类型名称引入别名的混淆;Vec不是类型的别名,不应作为 typedef-name。名称Vec是家庭的名称std::vector< [bullet] , MyAllocator< [bullet] > >- 其中项目符号是类型名称的占位符。因此,我们不建议使用“typedef”语法。另一方面,这句话

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

可以阅读/解释为:从现在开始,我将Vec<T>用作std::vector<T, MyAllocator<T> >. 通过这种阅读,别名的新语法似乎合乎逻辑。

I think the important distinction is made here, aliases instead of types. Another quote from the same document:

我认为这里有一个重要的区别,别名是es 而不是类型s。来自同一文件的另一个引用:

An alias-declaration is a declaration, and not a definition. An alias- declaration introduces a name into a declarative region as an alias for the type designated by the right-hand-side of the declaration. The core of this proposal concerns itself with type name aliases, but the notation can obviously be generalized to provide alternate spellings of namespace-aliasing or naming set of overloaded functions (see ? 2.3 for further discussion). [My note: That section discusses what that syntax can look like and reasons why it isn't part of the proposal.] It may be noted that the grammar production alias-declaration is acceptable anywhere a typedef declaration or a namespace-alias-definition is acceptable.

别名声明是声明,而不是定义。别名声明将名称引入声明区域,作为声明右侧指定的类型的别名。该提案的核心与类型名称别名有关,但该表示法显然可以泛化以提供命名空间别名或重载函数命名集的替代拼写(有关进一步讨论,请参见 2.3)。[我的笔记:该部分讨论了该语法的外观以及它不是提案的一部分的原因。] 可能需要注意的是,在任何可以接受 typedef 声明或命名空间别名定义的地方,都可以接受语法产生式别名声明。

Summary, for the role of using:

总结,对于角色using

  • template aliases (or template typedefs, the former is preferred namewise)
  • namespace aliases (i.e., namespace PO = boost::program_optionsand using PO = ...equivalent)
  • the document says A typedef declaration can be viewed as a special case of non-template alias-declaration. It's an aesthetic change, and is considered identical in this case.
  • bringing something into scope (for example, namespace stdinto the global scope), member functions, inheriting constructors
  • 模板别名(或模板 typedef,前者在名称上更受欢迎)
  • 命名空间别名(即namespace PO = boost::program_optionsusing PO = ...等效的)
  • 文件说A typedef declaration can be viewed as a special case of non-template alias-declaration。这是一种美学变化,在这种情况下被认为是相同的。
  • 将某些东西引入作用域(例如,namespace std引入全局作用域)、成员函数、继承构造函数

It cannotbe used for:

不能用于:

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

Instead do:

而是这样做:

using r = decltype(i);

Naming a set of overloads.

命名一组重载。

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