如何初始化指向模板化祖先数据的指针

How init pointer to data of templated ancestor

本文关键字:祖先 数据 指针 初始化      更新时间:2023-10-16

只是一个示例代码:

template <class T> class TempBase
{
protected:
  string m_str;
};
template <class T> class Temp: public TempBase<T>
{
public:
  void method()
  {
    string
        &s = TempBase<T>::m_str //reference works fine
        /*NOTE compile failed:
        error: ‘std::string TempBase<int>::m_str’ is protected
        error: within this context
        error: cannot convert ‘std::string TempBase<int>::* {aka std::basic_string<char> TempBase<int>::*}’ to ‘std::string* {aka std::basic_string<char>*}’ in initialization
        */
      , *ps = &TempBase<T>::m_str 
      , *ps2 = &m_str //compile failed, obviously: ‘m_str’ was not declared in this scope
      , *ps3 = &s //this is workaround, works fine
    ;
  }
};
void f()
{
  Temp<int> t;
  t.method();
}

目标: 具有祖先成员 TempBase<T>::m_strstd::string * 类型的 init 指针。

问题:语法正确未知

注释:以前的代码包含 2 个故意编译错误:

  1. 尝试将成员指针转换为数据指针
  2. 模板化祖先成员必须完全合格

和 1 个解决方法。

问题:在这种情况下,获取指向祖先数据的指针的正确语法是什么?

我将假设您想要的行为最好通过解决方法来描述

&s = TempBase<T>::m_str;

(您在问题中提供)而不是解决方法

&s = this->m_str;

这在您的示例中也有效。

解决方案:&(温度基准::m_str)

原因:TempBase::m_str

是限定 ID,(TempBase::m_str) 不是。

代码示例:

#include <iostream>
#include <string>
using namespace std;
template <class T> class TempBase
{
protected:
  string m_str;
};
template <class T> class Temp: public TempBase<T>
{
public:
  void method()
  {
    string* ps3 = &(TempBase<T>::m_str); //this is workaround, works fine
    (*ps3) += "ciao";
    cout << *ps3 << endl;
  }
};
void f()
{
  Temp<int> t;
  t.method();
}
int main( int argc, char* argv[] ) 
{
    f();
}

你可以在这里尝试

&m_str不起作用的原因:[temp.dep]/3

在类或类模板

的定义中,如果基类依赖于模板参数,则在非限定名称查找期间不会检查基类范围 [...]

但是,this->m_str表示从属名称(因为根据 [temp.dep.expr]/2 this从属名称)。在这种情况下,使用依赖名称查找来查找基类成员。

如果我们在类范围之外添加模板的专用化和名称,问题会更加明显:

string m_str;
template<class T> struct A { string m_str; };
template<> struct A<int> { /* no member */ };
template<class T> struct B : A
{
    void foo() { m_str = "hello"; }
};
B<int>().foo();    // clearly modifies the global `m_str`
B<double>().foo(); // modifies what?

如果搜索基类范围,则在实例化之前(在模板参数已知之前)不知道m_str引用的内容。此外,这很容易导致意外结果。

因此,不会搜索基类范围(如果基类是依赖的,并且我们处于"模板上下文"中)。


&s = TempBase<T>::m_str工作的原因:

id-expression TempBase<T>::m_str使用的是限定 id,因此搜索TempBase<T>的范围并找到成员m_str


&TempBase<T>::m_str不起作用但&(TempBase<T>::m_str)起作用的原因:[expr.unary.op]/3

一元&运算符的结果是指向其操作数的指针。操作数应为左值或限定 ID。如果操作数是一个限定 id,命名某个类C m T类型的非静态成员,则结果的类型为"指向类型 T 的类 C 的成员的指针",并且是指定C::m的 prvalue 。否则,如果类型 表达式T,结果的类型为"指向T的指针",并且是一个 prvalue 作为指定对象的地址 (1.7) 或指向指定函数的指针。

参数化表达式 (TempBase<T>::m_str) 不是限定 id,因此&(TempBase<T>::m_str)不构成指向成员的指针,而是指向 (TempBase<T>::m_str) 表示的对象的普通指针。