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


145

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

它在不同的情况下使用,我正在尝试查找所有这些功能是否具有相同之处,并且有一个原因使用“ 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

53
标准委托人讨厌将新关键字引入C ++语法。
互联网由catz组成

4
@tehinternetsismadeofcatz如果确实如此,请原谅我现在要自杀。
user3111311 2013年

62
@ user3111311:您确实认识到引入新的保留字的含义,对吗?这意味着所有使用这些名称作为标识符名称的现有代码都会突然无法编译。那是坏事。例如,有很多C代码无法编译为C ++,因为其中包含int class;。如果C ++代码突然停止为有效的C ++,那就更糟了。
Ben Voigt 2013年

7
@BenVoigt:使用C代码int class;不会像C ++那样编译的事实并不是一件坏事。它可以用来保证C代码将被编译为C。很容易忘记C和C ++是两种不同的语言-实际上,存在的代码是有效的C和有效的C ++,但是具有不同的语义。
基思·汤普森

1
在这方面,using没有比(更糟)更好的了static。恕我直言,不引入新关键字的观点非常重要,正如Internet由Catz和Ben Voigt解释的那样。
卡西欧·内里

Answers:


114

在C ++ 11中,using关键字用于时type alias与相同typedef

7.1.3.2

typedef名称也可以通过别名声明来引入。using关键字后面的标识符变为typedef名称,而标识符后面的可选attribute-specifier-seq属于该typedef名称。它具有与typedef说明符引入的语义相同的语义。特别是,它没有定义新的类型,也不应出现在type-id中。

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

在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提供了一个很好的理由。该标准希望尽可能避免破坏旧代码。这就是为什么在建议文件,你会看到喜欢的部分Impact on the StandardDesign decisions以及它们如何影响老年人的代码。在某些情况下,提案似乎是一个不错的主意,但可能没有吸引力,因为提案难以实施,过于混乱或与旧代码矛盾。


这是2003 n1449的一篇旧论文。基本原理似乎与模板有关。警告:由于从PDF复制而可能出现错别字。

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

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 template”一词,并引入涉及“ using”和“ =”对的新语法,以帮助避免混淆:我们此处未定义任何类型,我们引入了同义词(即别名)涉及模板参数的类型标识(即类型表达式)的抽象。如果在类型表达式中的可推导上下文中使用模板参数,则每当使用模板别名来形成模板ID时,都可以推导相应模板参数的值-接下来将进行更多介绍。在任何情况下,现在都可以编写可Vec<T>在可推断上下文中运行的通用函数,并且语法也得到了改进。例如,我们可以将foo重写为:

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

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


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

建议(重新)使用关键字typedef(如论文[4]所述)来引入模板别名:

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

该符号的优点是使用已知的关键字来引入类型别名。但是,它也显示了一些缺点,其中在别名不是指定类型而是模板的情况下,使用已知的关键字为类型名称引入别名的困惑。Vec不是类型的别名,并且不应将其用作typedef名称。名称Vec是家庭的名称std::vector< [bullet] , MyAllocator< [bullet] > > –项目符号是类型名称的占位符。因此,我们不建议使用“ typedef”语法。另一方面句子

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

可以理解为:从现在开始,我将Vec<T>用作的同义词std::vector<T, MyAllocator<T> >。通过阅读,别名的新语法似乎是合理的。

我认为重要区别在于别名 es而不是类型 s。同一文档中的另一句话:

别名声明是声明,而不是定义。别名声明将名称引入声明性区域,作为声明右侧指定的类型的别名。该建议的核心涉及类型名称别名,但显然可以将其概括为提供名称空间别名或重载函数的命名集的替代拼写(有关更多讨论,请参见第2.3节)。[ 我的注释:该部分讨论了语法的外观以及为什么它不属于提案的原因。可以注意到,语法生成别名声明在任何可接受的typedef声明或namespace-alias-definition都是可以接受的。

总结,作用using

  • 模板别名(或模板typedefs,以名称为准)
  • 命名空间的别名(即,namespace PO = boost::program_optionsusing PO = ...当量)
  • 文件说A typedef declaration can be viewed as a special case of non-template alias-declaration。这是一种美学上的变化,在这种情况下被认为是相同的。
  • 将某些东西带入范围(例如,namespace std全局范围),成员函数,继承构造函数

不能用于:

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

而是:

using r = decltype(i);

命名一组重载。

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

2
@ user3111311您还介意其他哪些关键字?“汽车”?“寄存器”?
Raymond Chen

2
using P = [](double)->void;AFAIK是无效的C ++ 11。但是,这是:using P = auto(double)->void;并产生函数类型(例如P*函数指针)。
dyp

2
他的名字叫Bjarne Stroustrup;)(请注意Stroustrup中的第二个r)
dyp

1
@RaymondChen:实际上register听起来不会那么糟,在:register X as Y
MFH

1
不幸的是register开始了一个变量声明,所以这已经有了含义。声明呼吁化Y型X的寄存器变量
雷蒙德陈
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.