提问者:小点点

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


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个答案

匿名用户

在C++11中,用于类型别名using关键字与typedef相同。

7.1.3.2

也可以通过别名声明引入typedef-name。using关键字后面的标识符成为typedef-name,标识符后面的可选属性-specifier-seq属于该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; // 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为不引入新关键字或新语法提供了一个很好的理由。该标准希望尽可能避免破坏旧代码。这就是为什么在建议文档中,您会看到诸如对标准的影响设计决策等部分,以及它们可能如何影响较旧的代码。有些情况下,一个建议看起来是一个很好的想法,但可能没有吸引力,因为它太难实现,太混乱,或者与旧的代码相矛盾。

这是一篇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”和“=”这对的新语法,以帮助避免混淆:我们在这里不定义任何类型,而是引入了涉及模板参数的type-id(即类型表达式)抽象的同义词(即别名)。如果在类型表达式中的可推演上下文中使用模板参数,那么无论何时使用模板别名来形成模板ID,都可以推导出相应模板参数的值--后面会有更多关于这方面的内容。无论如何,现在可以在可推论的上下文中编写操作vec的泛型函数,语法也得到了改进。例如,我们可以将foo改写为:

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

我们在这里强调,提出模板别名的一个主要原因是为了使参数推导和对foo(p)的调用能够成功。

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

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

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

这种表示法的优点是使用已知的关键字来引入类型别名。然而,它也显示了几个不可取之处,其中包括在别名不指定类型而是指定模板的上下文中使用已知的关键字为类型名引入别名的混乱;vec不是类型的别名,不应用作typedef名称。名称vec是系列std::vector<[bullet],myallocator<[bullet]>的名称,其中bullet>是类型名的占位符。因此,我们不建议使用“typedef”语法。另一方面,这句话

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

可以读/解释为:从现在起,我将使用vec作为std::vector>的同义词。这样看来,别名的新语法似乎合乎逻辑。

我认为重要的区别就在这里,别名而不是类型。另一段引用自同一文件:

别名声明是声明,而不是定义。别名声明将一个名称作为声明右侧指定的类型的别名引入声明性区域。这个建议的核心是关于类型名称别名,但是这个符号显然可以推广到提供名称空间别名或重载函数命名集的替换拼写(进一步讨论请参见2.3)。[我的注意:这一节讨论了该语法的外观以及它不是建议一部分的原因。]可以注意到,在可以接受typedef声明或namespace-alias-definition的地方,语法生成alias-definition都是可以接受的。

关于使用的角色摘要:

  • 模板别名(或模板typedefs,在名称方面首选前者)
  • 命名空间别名(即命名空间PO=boost::program_options和使用PO=...等效的)
  • 文档说可以将typedef声明视为非模板alias-declaration的一种特殊情况。这是一个美学上的变化,在本例中被认为是相同的。
  • 将某些内容带入作用域(例如,将命名空间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);

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|中|关键字|背后|逻辑)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?