c++ 正式来说,什么是typical?[副本]

j0pj023g  于 2023-10-21  发布在  其他
关注(0)|答案(8)|浏览(136)

此问题已在此处有答案

Where and why do I have to put the "template" and "typename" keywords?(10个答案)
上个月关门了。
有时我看到一些真正难以理解的错误信息吐出的gcc时使用模板.具体来说,我遇到过这样的问题:看似正确的声明会导致非常奇怪的编译错误,这些错误通过在声明的开头加上typename关键字前缀而神奇地消失了。(例如,就在上周,我声明了两个迭代器作为另一个模板类的成员,我必须这样做)。
typename的故事是什么?

z31licg0

z31licg01#

以下是Josuti的书中的引用:
引入关键字typename是为了指定后面的标识符是一个类型。请考虑以下示例:

template <class T>
Class MyClass
{
  typename T::SubType * ptr;
  ...
};

在这里,typename用于阐明SubTypeclass T的一种类型。因此,ptr是指向类型T::SubType的指针。如果没有typenameSubType将被视为静态成员。因此

T::SubType * ptr

将是T类型的值SubTypeptr的乘积。

kmbjn2e3

kmbjn2e32#

Stan Lippman's blog post建议:
Stroustrup重用了现有的class关键字来指定类型参数,而不是引入一个新的关键字,这当然可能会破坏现有的程序。这并不是说没有考虑新的关键字--只是考虑到它潜在的破坏性,它被认为没有必要。在ISO-C++标准之前,这是声明类型参数的唯一方法。
所以基本上Stroustrup重用了 class 关键字,而没有引入一个新的关键字,这个关键字后来在标准中被更改了,原因如下。
正如所举的例子

template <class T>
class Demonstration {
public:
void method() {
    T::A *aObj; // oops …
     // …
};

语言语法将T::A *aObj;误解为算术表达式,因此引入了一个新的关键字typename

typename T::A* a6;

它指示编译器将后面的语句视为声明。
既然关键字在工资单上,那么为什么不修复最初决定重用class关键字所造成的混乱呢?
所以我们两样都有
你可以看看this post。它肯定会帮助你。我只是尽我所能地从中提取。

k2arahey

k2arahey3#

考虑代码

template<class T> somefunction( T * arg )
{
    T::sometype x; // broken
    .
    .

不幸的是,编译器并不需要是通灵的,并且不知道T::sometype最终是否会引用类型名称或T的静态成员。所以,我们使用typename来告诉它:

template<class T> somefunction( T * arg )
{
    typename T::sometype x; // works!
    .
    .
7eumitmz

7eumitmz4#

在某些情况下,当你引用一个所谓的 dependent 类型的成员(意思是“依赖于模板参数”)时,编译器不能总是明确地推断出结果结构的语义含义,因为它不知道这是什么类型的名字(即,无论它是类型的名称、数据成员的名称还是其他事物的名称)。在这种情况下,你必须明确地告诉编译器这个名字属于一个被定义为依赖类型的成员的类型变量,从而消除这种情况的歧义。
例如

template <class T> struct S {
  typename T::type i;
};

在这个例子中,关键字typename是编译代码所必需的。
当你想引用一个依赖类型的模板成员时,也会发生同样的事情。指定模板的名称。您还必须使用关键字template来帮助编译器,尽管它的位置不同

template <class T> struct S {
  T::template ptr<int> p;
};

在某些情况下,可能需要同时使用

template <class T> struct S {
  typename T::template ptr<int>::type i;
};

(if语法正确)。
当然,关键字typename的另一个作用是用于模板参数声明。

cwdobuhd

cwdobuhd5#

秘密在于模板可以专门用于某些类型。这意味着它也可以为几种类型定义完全不同的接口。例如,你可以写:

template<typename T>
struct test {
    typedef T* ptr;
};

template<>         // complete specialization 
struct test<int> { // for the case T is int
    T* ptr;
};

有人可能会问为什么这是有用的,事实上:看起来真的没用。但是请记住,例如std::vector<bool>reference类型看起来与其他T类型完全不同。诚然,它不会将reference的类型从一种类型改变为不同的类型,但无论如何,它都可能发生。
现在,如果您使用这个test模板编写自己的模板,会发生什么呢?这样的事情

template<typename T>
void print(T& x) {
    test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

对你来说似乎没问题,因为你expecttest<T>::ptr是一个类型。但是编译器并不知道,事实上,标准甚至建议他期望相反的结果,test<T>::ptr不是一个类型。要告诉编译器你期望的是什么,你必须在前面添加一个typename。正确的模板如下所示

template<typename T>
void print(T& x) {
    typename test<T>::ptr p = &x;
    std::cout << *p << std::endl;
}

底线是:当你在模板中使用嵌套类型的模板时,你必须在之前添加typename。(当然,只有当您的模板的模板参数用于内部模板时。

tv6aics1

tv6aics16#

两种用途:
1.作为template参数关键字(而不是class

  1. typename关键字告诉编译器标识符是一种类型(而不是静态成员变量)
template <typename T> class X  // [1]
{
    typename T::Y _member;  // [2] 
}
odopli94

odopli947#

我想所有的答案都提到了typename关键字,用于两种不同的情况:
a)声明模板类型参数时。例如

template<class T> class MyClass{};        // these two cases are
template<typename T> class MyNewClass{};  // exactly the same.

它们之间没有区别,它们是完全一样的。
B)模板使用 * 嵌套依赖类型名称 * 之前。

template<class T>
void foo(const T & param)
{
   typename T::NestedType * value; // we should use typename here
}

不使用typename会导致解析/编译错误。
对于第二种情况,我想补充的是,在Scot Meyers的书Effective C++中提到的,在 * 嵌套依赖类型名称 * 之前使用typename是一个例外。例外的是,如果你使用 * 嵌套依赖类型名称 * 作为 * 基类 * 或在 * 成员初始化列表 * 中,你不应该在那里使用typename

template<class T>
class D : public B<T>::NestedType               // No need for typename here
{
public:
   D(std::string str) : B<T>::NestedType(str)   // No need for typename here
   {
      typename B<T>::AnotherNestedType * x;     // typename is needed here
   }
}

**注:**第二种情况使用typename(即在嵌套依赖类型名称之前)是不需要的,因为C++20。

o2gm4chl

o2gm4chl8#

#include <iostream>

class A {
public:
    typedef int my_t;
};

template <class T>
class B {
public:
    // T::my_t *ptr; // It will produce compilation error
    typename T::my_t *ptr; // It will output 5
};

int main() {
    B<A> b;
    int my_int = 5;
    b.ptr = &my_int;
    std::cout << *b.ptr;
    std::cin.ignore();
    return 0;
}

相关问题