CRTP 和由基 class 定义的类型的可见性
CRTP and visibility of a type that is defined by the base class
这里是一个有效代码的简短示例。它有助于介绍实际问题。
可见性的说明符与实际代码中使用的相同。
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() { }
它沿用了前面的示例,尽管稍作修改。
模板参数已添加到基础 class,派生参数已相应更新。
这个不编译。
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
应该是基础 class 的一部分,因此对派生对象可见,就像第一个示例中发生的那样。
它遵循一个例子,根据我对问题的猜测,实际编译但不应该,或者至少应该像前一个一样:
template <typename T>
struct B {
using foo = T;
};
struct D: public B<int> {
static foo bar;
};
int main() {
B<int> *b = new D;
}
会不会是因为派生的 class 在这种情况下不是模板化的?
老实说,这在我看来有点奇怪,因为 foo
类型是基础 class 的一部分,它仍然是模板化的,所以它应该与前一个没有太大区别。
不言而喻,我错了,但我想不出我的想法有什么问题。
提前感谢您的帮助。
那是因为名称查找规则。如果你的 base class 是一个模板,那么 base 中的非限定名称不会被解析。原因是稍后在您的代码中,您可能有该基的模板特化,它没有定义名称,或者名称的含义完全不同。对于编译器来说太复杂了,无法弄清楚你是否有专业化,如果有,你的名字在其中的含义是否相同。所以它更愿意推迟名称查找。要使编译器 "believe you",则需要使用限定名称,或者使用 this->name
(对于成员,而不是 typedef),编译器将解析该名称。
这里是一个有效代码的简短示例。它有助于介绍实际问题。
可见性的说明符与实际代码中使用的相同。
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() { }
它沿用了前面的示例,尽管稍作修改。
模板参数已添加到基础 class,派生参数已相应更新。
这个不编译。
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
应该是基础 class 的一部分,因此对派生对象可见,就像第一个示例中发生的那样。
它遵循一个例子,根据我对问题的猜测,实际编译但不应该,或者至少应该像前一个一样:
template <typename T>
struct B {
using foo = T;
};
struct D: public B<int> {
static foo bar;
};
int main() {
B<int> *b = new D;
}
会不会是因为派生的 class 在这种情况下不是模板化的?
老实说,这在我看来有点奇怪,因为 foo
类型是基础 class 的一部分,它仍然是模板化的,所以它应该与前一个没有太大区别。
不言而喻,我错了,但我想不出我的想法有什么问题。
提前感谢您的帮助。
那是因为名称查找规则。如果你的 base class 是一个模板,那么 base 中的非限定名称不会被解析。原因是稍后在您的代码中,您可能有该基的模板特化,它没有定义名称,或者名称的含义完全不同。对于编译器来说太复杂了,无法弄清楚你是否有专业化,如果有,你的名字在其中的含义是否相同。所以它更愿意推迟名称查找。要使编译器 "believe you",则需要使用限定名称,或者使用 this->name
(对于成员,而不是 typedef),编译器将解析该名称。