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

时间:2013-12-26 20:37:15

标签: c++ c++11

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

1 个答案:

答案 0 :(得分:77)

在C ++ 11中,用于using的{​​{1}}关键字与type alias相同。

7.1.3.2

  

也可以通过别名声明引入typedef-name。该   using关键字后面的标识符变为typedef-name和   标识符appertains后面的可选attribute-specifier-seq   到那个typedef-name。它具有与它相同的语义   由typedef说明符引入。特别是,它没有定义   一个新类型,它不应出现在type-id。

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

typedef

Pre-C ++ 11,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 关键字可以将成员函数纳入范围。在C ++ 11中,您现在可以为构造函数执行此操作(另一个Bjarne Stroustrup示例):

using

Ben Voight提供了一个很好的理由,它背后的理由是不引入新关键字或新语法。该标准希望尽可能避免破坏旧代码。这就是为什么在提案文档中,您会看到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) // ... }; Impact on the Standard等部分,以及它们可能会如何影响旧代码。有些情况下,提案似乎是一个非常好的主意,但可能没有牵引力,因为它实施起来太困难,太混乱,或者与旧代码相矛盾。


这是2003年n1449的一篇旧论文。理由似乎与模板有关。警告:由于从PDF复制而可能存在拼写错误。

  

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

Design decisions
     

这个成语的根本问题,以及主要的激励因素   对于这个提议,是成语导致模板参数   出现在不可导出的背景下。也就是说,它是不可能的   在不明确指定模板的情况下调用下面的函数foo   参数。

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
     

所以,语法有些难看。我们宁愿避免嵌套template <typename T> void foo (Vec<T>::type&);   我们更喜欢以下内容:

::type
     

请注意,我们特别避免使用术语“typedef template”和intr   引入涉及“using”和“=”对的新语法以帮助避免   困惑:我们在这里没有定义任何类型,我们正在介绍一个   用于抽象类型id的同义词(即别名)(即类型   表达式)涉及模板参数。如果是模板参数   在任何时候用于类型表达式的可推导上下文中   模板别名用于形成模板ID,值的   可以推导出相应的模板参数 - 更多关于这一点   跟随。无论如何,现在可以编写通用函数   它在可导入的上下文中以template <typename T> using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below Vec<int> p; // sample usage 运行,语法是   也改善了。例如,我们可以将foo重写为:

Vec<T>
     

我们在此强调提出的主要原因之一   模板别名是为了使参数推断和调用template <typename T> void foo (Vec<T>&);   会成功的。


后续文件n1489解释了为什么foo(p)而非使用using

  

有人建议(重新)使用关键字typedef - 就像在。中所做的那样   论文[4] - 介绍模板别名:

typedef
     

该符号具有使用已知关键字的优势   引入一个类型别名。但是,它也显示了几个   使用已知关键字的混淆的不利之处   在别名的上下文中引入类型名称的别名   不指定类型,而是指定模板; template<class T> typedef std::vector<T, MyAllocator<T> > Vec; 不是a的别名   type,不应该用于typedef-name。名称Vec是一个   家庭的姓名Vec    - 项目符号是类型名称的占位符。因此我们   不建议使用“typedef”语法。另一方面句子

std::vector< [bullet] , MyAllocator< [bullet] > >
     

可以被读取/解释为:从现在开始,我将使用template<class T> using Vec = std::vector<T, MyAllocator<T> >; 作为   Vec<T>的同义词。随着那个阅读,   别名的新语法似乎合理逻辑。

我认为这里有一个重要的区别, alias es而不是 type 。同一文件的另一个引用:

  

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

摘要,关于std::vector<T, MyAllocator<T> >的作用:

  • 模板别名(或模板typedef,前者是名字的首选)
  • 命名空间别名(即usingnamespace PO = boost::program_options等效)
  • 该文件说using 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);