::运算符在这种情况下会做什么

时间:2019-07-19 13:54:12

标签: c++ templates static-methods template-meta-programming

template <int N>
struct Factorial {
  static const int value = N * Factorial<N - 1>::value;
};

// Base case via template specialization:

template <>
struct Factorial<0> {
  static const int value = 1;
};

因此,我可能已经发现,'::'运算符的作用是以某种方式将较早执行的操作的内容(N *阶乘)馈入/添加到'value'变量中。但是有人可以更详尽地解释这一点吗(我不介意对'::'运算符角色的完整解释)。

非常感谢您!

7 个答案:

答案 0 :(得分:6)

::运算符称为范围解析运算符。它“解析”或明确了右侧操作数(变量名)所在的范围。

例如,std::cout使编译器清楚知道应该在名称空间cout中搜索标识符std。在您的情况下,Factorial<N - 1>::value清楚地表明value是模板类Factorial<N - 1>的成员。之所以使用它,是因为value是该类的static字段。

答案 1 :(得分:4)

  

所以我可能已经知道了

我强烈建议从书中学习 C ++,并学习使用现有的参考资料,而不是试图从基本原理中弄清楚某些代码的含义。如果您只是进行有根据的猜测,您很可能会犯一些细微的错误。

表达式Factorial<N - 1>::valuequalified identifier

Factorial<N - 1>是类的名称(Factorial<int>模板的实例化,其中单个参数具有特定值)。此类具有名为value的数据成员。需要明确资格,因为此特殊 value不在范围之内。另请参见qualified name lookup

您可以对任何类成员使用::这样的内容:例如std::string::npos大致意味着在以下位置的npos类中查找名为string的数据成员名为std 的命名空间。

  

...以某种方式将先前执行的操作的内容馈入/添加到“ value”变量中...

没有“更早的版本”,所有这些都发生在编译的同一阶段。

例如,我可以手动编写

struct F_0 { static const int value = 1; }
struct F_1 { static const int value = 1 * F_0::value; }
struct F_2 { static const int value = 2 * F_1::value; }
struct F_3 { static const int value = 3 * F_2::value; }

等等等,以获得我想要的尽可能多的值。模板化版本实际上是相同的,但是节省了大量输入。

具体来说,为Factorial<3>编写Factorial<int N> 实例化模板N=3,这意味着我们现在有了一个等效的具体非模板类Factorial<3>到我在上面手动编写的F_3

此类的定义引用Factorial<N-1>::value(带有N-1 = 2),因此Factorial<2>也被实例化。这一系列隐式实例化一直持续到我们到达显式专用的Factorial<0>为止(否则,它将一直尝试实例化Factorial<-1>Factorial<-2>,直到编译器放弃并失败为止。

答案 2 :(得分:2)

如果您的类foo具有静态成员bar,那么您可以refer to it with the :: notation作为foo::bar

在这里,Factorial<N - 1>是一个类,具有静态成员value。从模板符号来看,没有区别。

答案 3 :(得分:1)

struct Factorial模板类从value解析struct Factorial<N - 1>,每次value都递归解析。最后,根据以下基本情况,value将是1

template <>
struct Factorial<0>

value在这里确定:

Factorial<5> f;
std::cout << f.value << std::endl;

因此,输出将为:120

答案 4 :(得分:1)

static const int value = N * Factorial<N - 1>::value

这意味着成员Factorial<N>::value的值是N乘以成员Factorial<N-1>::value的值。 (Factorial<0>::value除外,它专门提供递归的基本案例

Factorial<N>Factorial<N-1>都是类。

::是作用域解析运算符,它告诉编译器您要访问其左侧类(或名称空间)的成员(在这种情况下为static成员)。

一个简单的例子:

class Foo
{
    static int x;
};

int Foo::x = 42;

int main()
{
   int y = Foo::x;  // access the member 'x' in the class 'Foo'
}

或:

#include <iostream>

int main()
{
   std::cout << "hi!\n";  // access the object 'cout' in the namespace 'std'
}

答案 5 :(得分:1)

为更清楚起见,请考虑对数据成员vector<bool>的替代访问。

value

程序输出为

#include <iostream>

template <int N>
struct Factorial {
  static const int value = N * Factorial<N - 1>().value;
};

// Base case via template specialization:

template <>
struct Factorial<0> {
  static const int value = 1;
};

int main()
{
    std::cout << Factorial<12>().value << '\n';
}

此处使用成员访问表达式479001600 Factorial<N - 1>().value

因此,要指定对数据成员Factorial<12>().value(特别是对于非静态数据成员)的访问,您可以创建类型为value或Factorial <12>的对象。

但是静态数据成员不需要创建该类的对象。因此,使用类名和运算符::指定对静态数据成员的访问更加简单安全。

成员访问表达式Factorial<N - 1>指定Factorial<N - 1>::value是类value的静态数据成员。类Factorial<N - 1>的两个对象都不是必需的。指定静态数据成员所属的类的名称就足够了。

答案 6 :(得分:-3)

“ ::-operator”用于访问名称空间(std :: string)中的内容或类中的静态内容:

struct C {
    static int i = 42;
};
int main() {
    std::cout << C::i << '\n';
}

这里C :: i是静态的,所以我不需要C类的实例。要访问类内部的一些非静态内容,请使用“。”。在您的示例中,Factorial不是类而是模板,Factorial是类,而Factorial :: value是Factorial类内部的静态int值。