C++:类型别名中的递归依赖
C++: recursive dependency in type alias
我想知道下面的代码是否正确。它在我的电脑上编译和运行,但我觉得在 ValueClass
中定义了 ShowValueClass
类型别名的递归依赖。您能否解释一下编译器是如何解决它的?
#include <iostream>
namespace tmp {
template <typename T>
struct ShowValueClass {
void ShowValue() { std::cout << T::value << std::endl; }
};
} // namespace tmp
struct ValueClass {
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
static constexpr int value = 42;
};
int main() {
ValueClass::ShowValueClass s;
s.ShowValue();
return 0;
}
这里没有递归。您的 class tmp::ShowValueClass<T>
仅适用于具有 value
成员 可打印 的任何类型 T
cout
(具有右ostream& operator<<(ostream&, const T&)
定义)。
您在 ValueClass
中添加了一个名为 ShowValueClass
的类型别名以引用 tmp::ShowValueClass<ValueClass>
的事实没有任何改变。在这种情况下,class 就像一个命名空间。
想想如果你从ValueClass
中提取ShowValueClass
会发生什么:
struct ValueClass {
static constexpr int value = 42;
};
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
在那种情况下,您将直接在 main()
中访问 ShowValueClass
,而不是像您的情况那样在其前面加上 ValueClass::
。 tmp::ShowValueClass
使用 T::value
和类型别名 ShowValueClass
在 ValueClass
中的事实都是 无关的 (它们没有什么特别之处).
使用 class 本身作为模板 class 的模板参数的整个想法在 C++ 中很普遍。实际上,有一种称为 CRTP(奇怪的重复模板模式)的模式,其中 class 继承自模板 class,使用 class 本身作为参数(来自维基百科):
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
您可以在此处查看整篇文章:https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
一个真正的递归依赖示例
现在,为了完成图片,我可以向您展示一些由于递归定义而无法编译的代码:
template <typename T>
struct A {
static constexpr int val = T::val;
};
struct B {
static constexpr int val = A<B>::val;
};
int main() { }
在这里,A<T>::val
依赖于 T::val
,这没关系,但是 B::val
依赖于 A<B>::val
,后者扩展为 B::val
。换句话说,B::val
依赖于B::val
,显然无法解决。就像这样说:
x := y(x)
其中 y(x)
为:
y(x) := x.
因此,x := x
。显然,无法确定 x
.
的值
使用模板递归的工作示例
现在,如果正确进行递归并且定义了 基本情况,显然它甚至可以用于计算非常复杂的事情。
这是一个简单的例子:
#include <iostream>
using namespace std;
template <int N>
struct A {
static constexpr int val = N + A<N - 1>::val;
};
template <>
struct A<0> {
static constexpr int val = 0;
};
int main() {
cout << A<10>::val << endl;
}
A<N>::val
递归定义为:
N + A<N-1>::val if N != 0
0 if N == 0
因此,它是从0
到N
(含)的所有数字的总和。
我想知道下面的代码是否正确。它在我的电脑上编译和运行,但我觉得在 ValueClass
中定义了 ShowValueClass
类型别名的递归依赖。您能否解释一下编译器是如何解决它的?
#include <iostream>
namespace tmp {
template <typename T>
struct ShowValueClass {
void ShowValue() { std::cout << T::value << std::endl; }
};
} // namespace tmp
struct ValueClass {
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
static constexpr int value = 42;
};
int main() {
ValueClass::ShowValueClass s;
s.ShowValue();
return 0;
}
这里没有递归。您的 class tmp::ShowValueClass<T>
仅适用于具有 value
成员 可打印 的任何类型 T
cout
(具有右ostream& operator<<(ostream&, const T&)
定义)。
您在 ValueClass
中添加了一个名为 ShowValueClass
的类型别名以引用 tmp::ShowValueClass<ValueClass>
的事实没有任何改变。在这种情况下,class 就像一个命名空间。
想想如果你从ValueClass
中提取ShowValueClass
会发生什么:
struct ValueClass {
static constexpr int value = 42;
};
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
在那种情况下,您将直接在 main()
中访问 ShowValueClass
,而不是像您的情况那样在其前面加上 ValueClass::
。 tmp::ShowValueClass
使用 T::value
和类型别名 ShowValueClass
在 ValueClass
中的事实都是 无关的 (它们没有什么特别之处).
使用 class 本身作为模板 class 的模板参数的整个想法在 C++ 中很普遍。实际上,有一种称为 CRTP(奇怪的重复模板模式)的模式,其中 class 继承自模板 class,使用 class 本身作为参数(来自维基百科):
// The Curiously Recurring Template Pattern (CRTP)
template<class T>
class Base
{
// methods within Base can use template to access members of Derived
};
class Derived : public Base<Derived>
{
// ...
};
您可以在此处查看整篇文章:https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
一个真正的递归依赖示例
现在,为了完成图片,我可以向您展示一些由于递归定义而无法编译的代码:
template <typename T>
struct A {
static constexpr int val = T::val;
};
struct B {
static constexpr int val = A<B>::val;
};
int main() { }
在这里,A<T>::val
依赖于 T::val
,这没关系,但是 B::val
依赖于 A<B>::val
,后者扩展为 B::val
。换句话说,B::val
依赖于B::val
,显然无法解决。就像这样说:
x := y(x)
其中 y(x)
为:
y(x) := x.
因此,x := x
。显然,无法确定 x
.
使用模板递归的工作示例
现在,如果正确进行递归并且定义了 基本情况,显然它甚至可以用于计算非常复杂的事情。 这是一个简单的例子:
#include <iostream>
using namespace std;
template <int N>
struct A {
static constexpr int val = N + A<N - 1>::val;
};
template <>
struct A<0> {
static constexpr int val = 0;
};
int main() {
cout << A<10>::val << endl;
}
A<N>::val
递归定义为:
N + A<N-1>::val if N != 0
0 if N == 0
因此,它是从0
到N
(含)的所有数字的总和。