• typename、typedef、using对比


    在c++的标准库中,因为类继承关系比较复杂和模板使用比较多的原因,源代码中充斥着typename、typedef和using这三个关键字

    一、typename关键字

    1.1、typename的第一个作用是用作模板里面,来声明某种类型

    1. template<typename _Tp, typename _Alloc>
    2. struct _Vector_base;

    最开始的时候声明模板形参,也会使用class,但我们都知道class总要是用来指定一个类名,据说是为了避免混淆,所以后来增加了typename这个关键字,它告诉编译器,跟在它后面的字符串是一个不确定的类型,而不是变量或者其他什么东西。

    1.2、typename在stl中还有另外一种作用

    例1

    1. //test.cpp
    2. #include
    3. using namespace std;
    4. template<typename _Tp, typename _Alloc>
    5. class AA
    6. {
    7. typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;
    8. };
    这里顺便说一下rebind前面为啥要放一个template,它是为了告诉编译器后面的<>是用于指定模板参数,而进行比较。

    这个时候我们使用g++ -c test.cpp -o test.o是可以编译通过的,但如果我们去掉第三个typename看,会发生什么呢?

    再次编译,报错如下:

    test.cpp:8:10: 错误:‘typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Tp>::other’之前需要‘typename’,因为‘typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Tp>’是一个有依赖的作用域
     typedef  __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;

    编译器直接指明了需要一个typename,实际上typename在这里也是指定它后面的字符串为类型,这是因为对于形如AA::BB这样的形式,它有可能是一个类型,也有可能是类的静态成员,这个时候加上typename就是为了告诉编译器,它后面的一大串字符串都是一个类型。

    1. template<typename T>
    2. void print2nd(const T& container)
    3. {
    4.   typename T::const_iterator * x; //①
    5.   ...
    6. }

    如果不加typename此时的编译器会纠结:const_iterator是一个什么东西?const_iterator是模板参数C内的一个static变量?x是一个全局变量?再或者:const_iterator是模板参数C内部的一个typedef。所以此时我们应该告诉编译器const_iterator是模板参数C里面的一个类型。即在C的前面加上typename就可以了

    事实上类型T::const_iterator依赖于模板参数T, 模板中依赖于模板参数的名称称为从属名称(dependent name), 当一个从属名称嵌套在一个类里面时,称为嵌套从属名称(nested dependent name)。 其实T::const_iterator还是一个嵌套从属类型名称(nested dependent type name)。

    嵌套从属名称是需要用typename声明的,其他的名称是不可以用typename声明的。

    例2

    1. template<typename T>
    2. class myFoo
    3. {
    4. public:
    5. using size_type = std::size_t;
    6. public:
    7. size_type size() const;
    8. private:
    9. size_type mSize = 5;
    10. };
    1. template<typename T>
    2. myFoo::size_type myFoo::size() const
    3. {
    4. return mSize;
    5. }

    这种写法仍然不对,也会被编译器无情的拒绝。

    因为,对于模板类,myFoo::size_type 这样的写法,会被认为是引用一个名为size_type的成员变量,而这里的size_type只是size_t的别名而已,是个类型名,而不是成员变量。

    答案最终揭晓,正确的写法是,保证对size_type的引用是类型名的引用,其实解决方法并不止一种:

    • 其一:使用typename关键字,具体代码如下:
    1. template<typename T>
    2. typename myFoo::size_type myFoo::size() const
    3. {
    4. return mSize;
    5. }
    • 其二:使用auto和decltype关键字,具体代码如下(包含模板类的声明部分):
    1. template<typename T>
    2. class myFoo
    3. {
    4. public:
    5. using size_type = std::size_t;
    6. public:
    7. auto size() const -> decltype(myFoo::mSize);
    8. private:
    9. size_type mSize = 5;
    10. };
    11. /** size成员函数实现 */
    12. template<typename T>
    13. auto myFoo::size() const -> decltype(myFoo::mSize)
    14. {
    15. return mSize;
    16. }

    二、typedef关键字

    typedef实际上就是给类型取了一个别名

    三、using关键字

    case1、 命名空间的使用

    case2、在子类中引用基类的成员(私有保护或者函数隐藏的情况)

    1. class T5Base {
    2. public:
    3. T5Base() :value(55) {}
    4. virtual ~T5Base() {}
    5. void test1() { cout << "T5Base test1..." << endl; }
    6. protected:
    7. int value;
    8. };
    9. class T5Derived : private T5Base {
    10. public:
    11. //using T5Base::test1;
    12. //using T5Base::value;
    13. void test2() { cout << "value is " << value << endl; }
    14. };

    基类中成员变量 value 是protected,在 private 继承之后,对于外界这个值为 private,也就是说T5Derived 的对象无法使用这个 value。

    如果想要通过对象使用,需要在public下通过 using T5Base::value 来引用,这样 T5Derived 的对象就可以直接使用。

    同样的,对于基类中的成员函数 test1(),在private继承后变为 private,T5Derived 的对象同样无法访问,通过 using T5Base::test1 就可以使用了。

    注意,using只是引用,不参与形参的指定。

    函数隐藏case:

    case3、 别名指定

    using在c++11以后又有了一种新的作用,那就是与typedef一样,给类型指定别名,形式是这样的:

    using 别名=类型;

    我们把上面typedef那里的代码改一下,如下

    对于函数指针,还可以这样:

    using func = int (*)(int a, int b);

    四、using 跟typedef有什么区别呢?哪个更好用些呢?

    1、可读性

    1. typedef std::unique_ptr> UPtrMapSS;
    2. using UPtrMapSS = std::unique_ptr>;
    3. typedef std::string (Foo::* fooMemFnPtr) (const std::string&);
    4. using fooMemFnPtr = std::string (Foo::*) (const std::string&);

    2、alias templates, 模板别名。

    1. template <typename T>
    2. using Vec = MyVector>;
    3. // usage
    4. Vec<int> vec;

    这一切都会非常的自然。
    那么,若你使用typedef来做这一切:

    1. template <typename T>
    2. typedef MyVector> Vec;
    3. // usage
    4. Vec<int> vec;

    当你使用编译器编译的时候,将会得到类似:error: a typedef cannot be a template 的错误信息。

    虽然两者都用于创建类型别名,但“typedef”的主要限制是它不适用于模板。当涉及到非模板工作时,“using”和“typedef”在机械上都是相同的。因此,在这种情况下,这是程序员的个人选择。

    五、调用模板成员函数需不需要加template关键字

  • 相关阅读:
    uCharts常用图表组件demo
    21-关键帧动画
    Blazor和Vue对比学习(基础1.7):传递UI片断,slot和RenderFragment
    python --机器学习(基本算法详解)
    Python MQTT订阅消息QOS=1的注意点
    [ELK实战] 开发中的哪些坑
    Spring事务传播机制
    微信小程序,连续播放多段视频。合成一个视频的样子,自定义视频进度条
    11. SpringBoot项目中参数获取与响应
    代码格式化----逗号问题
  • 原文地址:https://blog.csdn.net/sinat_27652257/article/details/133700751