c++ - 为什么这些 CRTP 模式中只有一种可以编译?
问题描述
考虑以下两段具有CRTP模式的代码:
template <typename Derived>
struct Base1 {
int baz(typename Derived::value_type) {
return 42;
}
};
struct Foo1 : Base1<Foo1> {
using value_type = int;
};
template <typename Derived>
struct Base2 {
auto baz() {
return typename Derived::value_type {};
}
};
struct Foo2 : Base2<Foo2> {
using value_type = int;
};
第一个编译失败,而第二个编译。我的直觉说他们应该要么编译要么都不编译。现在,如果我们用显式类型替换auto
in Base2
:
template <typename Derived>
struct Base3 {
typename Derived::value_type baz() {
return typename Derived::value_type {};
}
};
struct Foo3 : Base3<Foo3> {
using value_type = int;
};
它不再编译;但我真的不明白这里有什么大区别。这是怎么回事?
注意:这出现在 David S. Hollman 的闪电演讲中,关于奇怪重复模板模式的思考,在 C++-Now 2019 中。
解决方案
类型Foo1
仅在末尾完成};
struct Foo1 : Base1<Foo1> {
// still incomplete
} /* now complete */;
但是在Foo1
开始被定义之前,它必须首先实例化基类以使基类完整。
template <typename Derived>
struct Base1 {
// Here, no type are complete yet
// function declaration using a member of incomplete type
int baz(typename Derived::value_type) {
return 42;
}
};
在基类体内,还没有一个类是完整的。你不能在那里使用嵌套的类型名。定义类类型时,声明必须全部有效。
在成员函数体内,情况有所不同。
就像这段代码不起作用:
struct S {
void f(G) {}
using G = int;
};
但是这个没问题:
struct S {
void f() { G g; }
using G = int;
};
在成员函数体内,所有类型都被认为是完整的。
auto
那么......如果返回类型推断为您无法访问的类型,为什么它会起作用?
auto
返回类型确实很特殊,因为它允许前向声明具有推导返回类型的函数,如下所示:
auto foo();
// later
auto foo() {
return 0;
}
因此,auto 的扣除可用于延迟声明中不完整的类型的使用。
如果auto
是瞬时推导的,则函数主体中的类型不会像规范所暗示的那样完整,因为它必须在定义类型时实例化函数主体。
至于参数类型,它们也是函数声明的一部分,所以派生类还是不完整的。
虽然不能使用不完整类型,但可以检查推导的参数类型是否真的是typename Derived::value_type
.
即使实例化的函数接收typename Derived::value_type
(当使用正确的参数集调用时),它也只在实例化点定义。到那时,类型就完成了。
有一些类似于自动返回类型的东西,但对于参数来说,这意味着一个模板:
template<typename T>
int baz(T) {
static_assert(std::is_same_v<typename Derived::value_type, T>)
return 42;
}
只要您不直接使用声明中不完整类型的名称,就可以了。您可以使用诸如模板或推导返回类型之类的间接方法,这将使编译器满意。
推荐阅读
- json - Jmeter解析并断言Json对象数组
- categories - Pandas groupby 适用于多索引性能问题
- minimize - 多目标优化示例 Pyomo
- c# - 如何在 Visual Studio 2017 中手动添加 DiagnosticAnalyzer
- javascript - React Native - Deeplink 不适用于 android 的发布版本
- asp.net - 通过 ADFS 登录后,asp.net Microsoft.IdentityModel.Tokens.SecurityTokenException “找不到验证器”
- c# - Angular 5 和 ASP.NET Core 2.0 Web API 文件上传 - 文件损坏
- reactjs - 如何在 DrawerNavigator 中有条件地显示drawerLabel
- php - move_uploaded_file():无法将“/tmp/php4l8IAc”移动到上传目录
- javascript - 如何创建具有自己的 css 和 javascript 的用户控件