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

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

本文关键字:是什么 背后 using 关键字 C++      更新时间:2023-10-16

>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

在 C++11 中,用于type aliasusing关键字与typedef相同。

7.1.3.2

类型定义名称也可以通过别名声明引入。这 using 关键字后面的标识符成为 typedef-name,并且 标识符后面的可选属性说明符序列 到该类型定义名称。它具有相同的语义,就好像它是 由 Typedef 说明符引入。特别是,它没有定义 新类型,它不应出现在类型 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; // not valid in C++11
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 模板"和 引入涉及"using"和"="对的新语法,以帮助避免 混淆:我们在这里没有定义任何类型,我们引入了一个 类型ID抽象的同义词(即别名)(即类型 表达式)涉及模板参数。如果模板参数 在类型表达式的可推导上下文中使用,然后每当 模板别名用于形成模板 ID,即 可以推导出相应的模板参数 - 更多关于此将 跟随。无论如何,现在可以编写泛型函数 在可推导的上下文中对Vec<T>进行操作,语法为 也得到了改进。例如,我们可以将foo重写为:

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

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


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

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

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

该表示法的优点是使用已知 引入类型别名。但是,它还显示几个 其中使用关键字的混淆已知 在别名的上下文中引入类型名称的别名 不是指定类型,而是指定模板;Vec不是 的别名 类型,并且不应用于类型定义名称。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 的任何地方都是可以接受的 声明或命名空间别名定义是可以接受的。

总结,对于using的作用:

模板
  • 别名(或模板类型定义,前者首选名称)
  • 命名空间别名(即namespace 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);

命名一组重载。

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

typedefusing之间的另一个区别: 你可以这样做:

using vector3d_t = double[3];
vector3d_t v = {1,2,3};
v[1] = 4;

使用typedef是不可能的:

typedef double[3] vector3d_t; // Compilation error