依赖模板的基成员没有正确解析

Template dependent base member is not resolved properly

本文关键字:成员 依赖      更新时间:2023-10-16

这个问题是将成员函数从基类移到派生类没有明显原因地破坏程序(这是为什么不应该使用using namespace std;的一个主要例子)

,其中答案建议通过this->限定依赖模板名称(这确实是引用此类依赖成员时的方法)。但是,似乎有一个问题,所以我将列出一个重现该问题的最小示例。

考虑以下代码:

#include <iostream>
#include <bitset>
using namespace std;
template<class T>
struct B
{
    T bitset{};
};
template<class T>
struct D : B<T>
{
    bool foo()
    {
        return this->bitset < 32; 
    }
};
int main(){}

Live on Coliru

令人费解的是,即使this->bitset应该引用成员B<T>::bitset,编译器仍然混淆,认为我们试图引用std::bitset<std::size_t>。这个错误出现在gcc6和clang3.7上。知道为什么会这样吗?但是用B<T>::bitset限定它是可以的。

错误(逐字):

In member function 'bool D<T>::foo(T, std::__cxx11::string)': cpp/scratch/minimal.cpp:24:22: error: invalid use of 'class std::bitset<1ul>'

编辑

在我看来,这像是一个解析/名称查找错误。如果我们用任何其他比较运算符替换<(感谢@Leon的注释),例如

return this->bitset == 32; 

程序编译。所以我猜在this->bitset < 32中,解析器认为我们正在尝试实例化一个模板(<符号),而我们忘记关闭>。但是我也不知道这到底是一个bug还是语言应该是这样工作的

tl;dr看起来这是一个深思熟虑的决定,特别是为了支持您已经使用的替代语法。

下列标准的大致演练:

this-> B <
         ^
  • 可以是模板id的开头,也可以是小于,所以让我们检查一下!
    1. this->B确实命名了一些东西,但它是一个模板B<T>,所以继续
    2. B本身也命名了一些东西,一个类模板B<T>
    3. 等等,它们是一样的!这意味着我们使用this->B<T>作为限定符,它毕竟不是小于

另一种情况下,

this->bitset

以相同的方式执行,直到第三步,当它意识到有两个不同的东西叫做bitset(一个模板类成员和一个类模板)时,就放弃了。


这是我的工作草案,所以不一定是最近的,但是:

3.4.5类成员访问[basic.lookup]。classref]

1在类成员访问表达式(5.2.5)中,表示或者-> token是立即在标识符后面加上<</em>,标识符必须是查找以确定是否有<是模板的开头吗实参列表(14.2)或小于操作符。标识符是第一位的在类中查找对象表达式。如果标识符为没有找到,则在整个上下文中查找后缀表达式和应该命名一个类模板。>如果查找在对象表达式的类找到模板,其名称也为在整个后缀表达式和

的上下文中查找
  • 如果没有找到该名称,则使用对象表达式类中找到的名称,否则
  • 如果该名称在整个后缀表达式的上下文中找到,并且没有命名类模板,使用对象表达式类中找到的名称,否则
  • 如果找到的名字是一个类模板,它应该和在类中找到的名字指向同一个实体对象表达式,否则程序是病态的。

因此,在任何像this->id < ...这样的表达式中,它必须处理id<...是模板标识符的开始(如this->B<T>::bitset)的情况。

它仍然首先检查对象,但如果this->id找到模板,则应用进一步的步骤。在您的例子中,this->bitset可能被认为是一个模板,因为它仍然依赖于T,所以它发现冲突的std::bitset并在上面的第三个项目中失败。