代码之家  ›  专栏  ›  技术社区  ›  Prasoon Saurav

模板C++中的作用域问题

  •  1
  • Prasoon Saurav  · 技术社区  · 14 年前

    这个程序有范围问题吗?

    #include<iostream>
    
    using namespace std;
    
    template<class Type>
    class Base
    {
       public:
       Type member;
       Base(Type param): member(param){
    
       }
    };
    
    template<class Type>
    class Derived: public Base<Type>
    {
        public:               
        Derived(Type param):Base<Type>(param){
         //                       ^
         //                       |_______  Not writing Type here gives error, why?
        }
        void display()
        {
            cout << member; /** ERROR HERE **/
        }
    };
    
    int main()
    {
       Derived<int> p(5);
       p.display();
       return 0;
    }
    

    我得到错误 'member' was not declared in this scope . 如何解决问题?

    5 回复  |  直到 13 年前
        1
  •  10
  •   sbi    13 年前

    你的问题有点令人困惑。起初我以为你在问 base<Type> 在成员初始化列表中,我以为您在询问访问 member ,然后回到第一个…现在我想你是在问两个问题,所以我会回答两个问题。


    这里不写类型会出错,为什么?

    当使用类模板的名称时( my_class_templ )是指 模板 ,它不是类型。为了将其用作类型,需要提供模板参数( my_class_templ<int> , my_class_templ<T> )因此,只要需要类型名(在初始化列表中包括基类名),就需要提供模板参数。

    可以省略模板参数列表 用于类模板定义中的类模板名称。例如,复制构造函数可以声明为

     my_class_templ(const my_class_templ& rhs);
    

    而不是

     my_class_templ<T>(const my_class_templ<T>& rhs);
    

    这只是一点句法上的甜头,允许你少打字。

    但是,在类模板定义之外,您需要显式地拼出所有模板参数。对于派生类也是如此:

    my_dervied_class_templ(const my_derived_class_templ& rhs)
     : my_class_templ<T>(rhs)                          // need to spell out <T> here
    {
    }
    

    我得到错误 'member' was not declared in this scope . 如何解决问题?

    当编译器首先遇到您的模板时,只有它的定义,而且编译器还没有看到任何实例化。编译器不知道在实例化点上模板是否在作用域内有专门化。但是,您可以将模板专门用于 Base<T>::member 指代或不完全指代别的东西。(比如说,专业化 Base<void> 没有数据成员。)因此,编译器不能推测 Base . 因此,他们不会被 Derived .

    结果是,如果您需要引用 基地 ,您需要告诉编译器您希望 Base<T> 有这样一个成员。这是通过完全限定其名称来完成的: Base<Type>::member .

        2
  •  8
  •   Prasoon Saurav    14 年前

    这里不写类型会出错,为什么?

    如果省略 Type 编译器无法决定 Base 是基类还是其成员 Derived . 指定 类型 确保 基地 是模板类[基类]。

    “member”未在此范围中声明

    这与名称查找规则(依赖的基类)有关。

    C++ 03〔14.6/8〕说

    在查找模板定义中使用的名称声明时,通常的查找规则(3.4.1、3.4.2)用于非依赖名称。依赖于模板参数的名称查找是 推迟到实际模板参数已知为止 (14。6)。

    现在 Section 14.6.2/3

    在类模板或类模板成员的定义中, 如果类模板的基类依赖于模板参数,则在类模板或成员的定义点或类模板或成员的实例化期间,不会在非限定名称查找期间检查基类范围。

    member 是非限定名,因此不检查基类。

    所以你有两个选择。

    1. 使用的完全限定名 Member Base<Type>::member
    2. 使用 this->member .
        3
  •  2
  •   Alexandre C.    14 年前

    在编译器读取模板的时候(而不是当它声明模板的时候),它无法分辨出 Base<Type> 是(它可以是专门化的),因此不尝试推断它有一个 member 成员。你必须明确地告诉它: cout << this->Base<Type>::member; .

    我认为(检查一下,我不确定) using Base<Type>::member 在类范围内也可以工作。

        4
  •  2
  •   sellibitze    14 年前

    C++标准要求编译器对模板进行“两阶段查找”。也就是说,当解析模板时,他们试图解析第一阶段中的所有非依赖名称(不依赖于模板参数的名称),而当实例化模板时,他们试图解析第二阶段中的所有依赖名称。

    如果你没有资格 member 它被视为一个非依赖名称,在第一个阶段查找失败。你可以通过预先准备来解决这个问题。 this-> 对它。这使得 成员 依赖名称和查找被延迟,直到您实际实例化模板。

        5
  •  0
  •   Magnus Hoff    14 年前
    Derived(Type param):Base<Type>(param){ 
    

    Base<Type> 是必需的,因为派生的基是 Base<T> . 什么都没叫 Base .

    void display()   
    {   
            //cout << member; /** ERROR HERE **/ 
            cout << this->member;
            cout << this>Base<Type>::member;  
    }
    

    或者,在“派生”范围内具有using声明也是一种有效的技术。