CRTP和由基类定义的类型的可见性

时间:2015-12-04 21:31:42

标签: c++ templates visibility crtp

这是一个有效的代码的简短示例。它有助于介绍实际问题 可见性的说明符与实际代码中使用的相同。

class Base {
public:
    using foo = int;
    virtual ~Base() { }

protected:
    static foo bar() noexcept {
        static foo v = 0;
        return v++;
    }
};

template<class Derived>
class Class: public Base {
    static foo get() noexcept {
        static foo v = bar();
        return v;
    }
};

int main() { }

它遵循前面的例子,即使稍作修改 模板参数已添加到基类中,派生的模板参数已相应更新 这个不编译。

template<typename T>
class Base {
public:
    using foo = int;
    virtual ~Base() { }

protected:
    static foo bar() noexcept {
        static foo v = 0;
        return v++;
    }
};

template<class Derived, typename T>
class Class: public Base<T> {
    static foo get() noexcept {
        static foo v = bar();
        return v;
    }
};

int main() { }

错误确实非常清楚,问题不在于解决它:

main.cpp:18:12: error: ‘foo’ does not name a type
     static foo get() noexcept {
            ^
main.cpp:18:12: note: (perhaps ‘typename BaseComponent<T>::foo’ was intended)

实际问题是:如果没有第二个例子中的范围说明符,为什么foo不可见? 我的意思是,Class派生自Base<T>,这是(至少在我看来)一个完全定义的类型,因此foo应该是基类的一部分,因此对派生可见一,就像第一个例子中那样。

它遵循一个例子,根据我对这个问题的猜测,实际编译并且不应该,或者至少应该像前一个那样:

template <typename T>
struct B {
    using foo = T;
};

struct D: public B<int> {
    static foo bar;
};

int main() {
    B<int> *b = new D;
}

可能是因为在这种情况下派生类不是模板化的类吗? 老实说,在我看来有点奇怪,因为foo类型是基类的一部分仍然是模板的类型,所以它与前一类不应该有太大的不同。

不言而喻,我错了,但我无法弄清楚我的想法有什么不妥。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

这是因为名称查找规则。如果基类是模板,则不会解析基础中的非限定名称。原因是,在您的代码中稍后您可能具有该基础的模板特化,该模板不定义名称,或者名称意味着完全不同的名称。编译器弄清楚你是否有专业化是太复杂了,如果是这样,你的名字是否意味着同样的东西。所以它更喜欢推迟名称查找。要使编译器“相信你”,那么你需要使用限定名,或者this->name代替(对于成员,而不是typedef),编译器将解析名称。