小编典典

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

all

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

阅读 161

收藏
2022-07-07

共1个答案

小编典典

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

7.1.3.2

typedef-name 也可以由别名声明引入。using 关键字之后的标识符成为 typedef-name,标识符之后的可选属性说明符序列属于该
typedef-name。它具有与由 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

在 C11 之前,using关键字可以将成员函数带入作用域。在 C11 中,您现在可以对构造函数执行此操作(另一个 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 Standard、的部分Design 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

请注意,我们特别避免使用术语“ypedef
模板”,并引入涉及“sing”和“=涡”对的新语法,以帮助避免混淆:我们在这里没有定义任何类型,而是引入了同义词(即别名)用于涉及模板参数的类型标识(即类型表达式)的抽象。如果模板参数在类型表达式的可推导上下文中使用,那么无论何时使用模板别名来形成模板
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不是类型的别名,不应作为
typedef-name。名称Vec是族的名称std::vector< [bullet] , MyAllocator< [bullet] > >
——其中项目符号是类型名称的占位符。因此,我们不建议使用“ypedef”语法。另一方面,句子

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

可以阅读/解释为:从现在开始,我将Vec<T>用作 . 的同义词`std::vector<T, MyAllocator

`。通过这种阅读,别名的新语法似乎是合乎逻辑的。

我认为重要的区别就在这里, 别名 es 而不是 type s。同一文件中的另一个引述:

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

总结,对于角色using

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