我想知道以下代码是否正确。它可以在我的计算机上编译并运行,但是我觉得在ShowValueClass
中定义了ValueClass
类型别名的递归依赖性。您能解释一下编译器如何解决它吗?
#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;
}
答案 0 :(得分:2)
这里没有递归。您的班级tmp::ShowValueClass<T>
可以用于任何类型T
的成员value
可以由cout
进行可打印的成员(具有正确的ostream& operator<<(ostream&, const T&)
定义)。
您在ShowValueClass
内添加了引用ValueClass
的类型别名tmp::ShowValueClass<ValueClass>
的事实没有改变。在这种情况下,类的行为就像命名空间一样。
请考虑一下,如果您从ShowValueClass
中提取了ValueClass
:
struct ValueClass {
static constexpr int value = 42;
};
using ShowValueClass = tmp::ShowValueClass<ValueClass>;
在这种情况下,您将直接在ShowValueClass
中访问main()
,而不是像在您的情况那样以ValueClass::
为前缀。 tmp::ShowValueClass
使用T::value
和ShowValueClass
中的类型别名ValueClass
都是不相关(它们没有什么特别的)。 / p>
在C ++中广泛使用类本身作为模板类的模板参数的整个想法。实际上,存在一种称为CRTP(好奇地重复模板模式)的模式,其中,一个类从模板类继承而来,使用类本身作为参数(例如,来自Wikipedia):
// 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
(包括两端)的所有数字的总和。