Mq-b/Modern-Cpp-templates-tutorial

待决名查找规则中的示例解释

Closed this issue · 3 comments

Mq-b commented

原文:

对于在模板的定义中所使用的非待决名,当检查该模板的定义时将进行无限定的名字查找。在这个位置与声明之间的绑定并不会受到在实例化点可见的声明的影响。而对于在模板定义中所使用的待决名它的查找会推迟到得知它的模板实参之时。此时,ADL 将同时在模板的定义语境和在模板的实例化语境中检查可见的具有外部连接的 (C++11 前)函数声明,而非 ADL 的查找只会检查在模板的定义语境中可见的具有外部连接的 (C++11 前)函数声明。(换句话说,在模板定义之后添加新的函数声明,除非通过 ADL 否则仍是不可见的。)如果在 ADL 查找所检查的命名空间中,在某个别的翻译单元中声明了一个具有外部连接的更好的匹配声明,或者如果当同样检查这些翻译单元时其查找会导致歧义,那么行为未定义。无论哪种情况,如果某个基类取决于某个模板形参,那么无限定名字查找不会检查它的作用域(在定义点和实例化点都不会)。
很长,但是看我们加粗的就够:

  • 非待决名:检查该模板的定义时将进行无限定的名字查找
  • 待决名:它的查找会推迟到得知它的模板实参之时

我们这里简单描述一下:

this->f() 是一个待决名,这个 this 依赖于模板 X

所以,我们的问题可以解决了吗?

  1. this->f() 是待决名,所以它的查找会推迟到得知它模板实参之时(即知道父类是谁,可以访问父类)。
  2. f() 是非待决名,检查该模板的定义时将进行无限定的名字查找(不知道父类),按照正常的查看顺序,先类内(查找不到),然后全局(找到)。

是否需要修改掉 “即知道父类是谁,可以访问父类”、“不知道父类”这种不严谨的说法。

我感觉其实是上面概念中的最后一句话:

无论哪种情况,如果某个基类取决于某个模板形参,那么无限定名字查找不会检查它的作用域

这句话可以解释为什么 f() 非待决名进行无限定名字查找找不到父类,因为它取决于模板形参,还未实例化。

this->f() 是待决名,查找会推迟到得知它模板实参之时,此时父类不再取决于模板形参,已经实例化了,就没有上面说的那个问题了。

这是否合理?

即知道父类是谁,可以访问父类

不知道父类

我有点想改成

届时可以确定父类是否有 f 函数

无法查找父类的定义

注意我们需要区别基类是否为待决的:

template<class T>
struct base {
    static constexpr bool get_value() noexcept { return true; }
};

template<class T>
struct derived1 : base<int> {
    void test() const
    {
        static_assert(get_value()); // OK
    }
};

template<class T>
struct derived2 : base<T> {
    void test() const
    {
        // static_assert(get_value()); // Bad
        static_assert(this->get_value()); // this-> required
    }
};

还有就是我们可能应该说 this->f() 中的 f 是待决名,而非整个构造是待决名。 this->f() 整体是待决调用。

Mq-b commented

届时可以确定父类是否有 f 函数

无法查找父类的定义

很好。

还有就是我们可能应该说 this->f() 中的 f 是待决名,而非整个构造是待决名。 this->f() 整体是待决调用。

“应该说 this->f() 中的 f 是待决名”,但是这不好描述 f()this->f() 调用的区别,容易让人理解为单独的 f() 也是待决名。