【问题标题】:Template dependent base member is not resolved properly模板依赖的基础成员未正确解析
【发布时间】:2016-11-07 17:06:33
【问题描述】:

这个问题是后续的 Moving a member function from base class to derived class breaks the program for no obvious reason(这是为什么不应该使用 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-&gt;bitset 应该引用成员B&lt;T&gt;::bitset,编译器仍然感到困惑,并认为我们试图引用std::bitset&lt;std::size_t&gt;。该错误出现在 gcc6 和 clang3.7 上。任何想法为什么会发生这种情况?不过,使用B&lt;T&gt;::bitset 对其进行限定是可行的。

错误(逐字):

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

编辑

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

return this->bitset == 32; 

程序编译。所以我猜在this-&gt;bitset &lt; 32 中,解析器认为我们正在尝试实例化一个模板(&lt; 符号),而我们忘记关闭&gt;。但是再次不知道这是否确实是一个错误,或者这就是该语言的工作方式。

【问题讨论】:

  • 我认为编译器只是在巧妙地告诉您,您确实应该使用 std::bitset ;) 而不是重新调整用途的整数。
  • 可能是因为name-lookup 的工作方式? name-lookup 在一个简单的案例中通过检查派生命名空间,然后是基本命名空间,然后是全局范围来工作。因为这里的Base 是依赖的,所以它无法查看它的范围并找出它,直到它找到一个实例化。
  • @Arunmu:我希望在数据成员和成员函数的情况下推迟名称查找;这似乎有所不同。
  • @MatthieuM。我认为Arunmu可能是对的。即使main() 为空,我们仍然会收到错误消息。因此错误出现在辅助名称查找之前。我得回josuttis.com/tmplbook看看具体的规则是什么:)
  • @Pixelchemist 是的,我知道,你甚至不需要this-&gt;,我一般也不使用using namespace。问题是为什么非限定名称不起作用,因为它看起来是一个非常奇怪的解析问题。

标签: c++ c++11 templates


【解决方案1】:

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

以下标准语的大致演练:

this-> B <
         ^
  • 这可能是模板 ID 的开头,也可能是小于号,所以让我们同时检查一下!
    1. this-&gt;B 确实命名了一些东西,但它是一个模板 B&lt;T&gt;,所以继续前进
    2. B 它自己也命名了一些东西,一个类模板 B&lt;T&gt;
    3. 等等,它们是一样的!这意味着我们使用this-&gt;B&lt;T&gt; 作为限定符,而且它并不是毕竟小于

在另一种情况下,

this->bitset

继续相同直到第三步,当它意识到有两个不同的东西叫做bitset(一个模板类成员和一个类模板),然后给出起来。


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

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

1 在类成员访问表达式 (5.2.5) 中,如果 .或 -> 令牌立即 后跟一个标识符,后跟一个 ,标识符必须是 查找以确定 如果查找 对象表达式的类找到模板,名字也是 在整个后缀表达式的上下文中查找和

  • 如果找不到名称,则使用在对象表达式的类中找到的名称,否则
  • 如果在整个后缀表达式的上下文中找到该名称并且没有命名类模板, 使用在对象表达式的类中找到的名称,否则
  • 如果找到的名称是类模板,则它应指与在类中找到的实体相同的实体 对象表达式,否则程序格式错误。

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

它仍然首先检查对象,但如果this-&gt;id 找到模板,则应用进一步的步骤。在您的情况下,this-&gt;bitset 可能被认为是一个模板,因为它仍然依赖于T,因此它会找到冲突的std::bitset,并在上面的第三个项目符号处失败。

【讨论】:

  • 谢谢,我得说这是我在 C++ 代码中见过的最模糊的错误之一。
  • 同意,这有点令人惊讶。大概在某个阶段,如果没有某种规则,就不可能确定表达式的含义。
  • 我猜一开始就有一个模棱两可的语法,在某些时候你只需要退出:/
  • 是的,最初的(链接的)问题可能很好地证明了为什么提早纾困比继续吃代币并希望它最终有意义...
  • 正如缺陷报告所说,问题始于(在我们的示例中)bitset 是一个从属名称。我认为这就是 template 消歧器的用途,例如this-&gt;template bitset&lt; 如果您想避免将 &lt; 视为小于运算符。
猜你喜欢
  • 2019-08-19
  • 2021-08-09
  • 1970-01-01
  • 2010-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-02
相关资源
最近更新 更多