2011-08-25 76 views
4

我正在阅读本书,C++模板:完整指南。在一个对我卡住了无法理解的术语,这里是第:C++模板中的一些混淆

一个基本原则是,任何模板参数必须是可以在编译时确定的 数量或价值。随着后来变得更清晰,这个要求转化为模板实体的运行时成本的巨大收益。 由于模板参数 最终被编译时值替代,所以它们本身可以使用 来形成编译时表达式。这在ArrayInClass模板 中被利用来调整成员数组数组的大小。数组 的大小必须是所谓的常量表达式,并且参数N的模板同样适用。

我们可以把这种推理远一点:因为模板 参数是编译时的实体,它们也可以被用来创建 有效模板参数。下面是一个例子

template <typename T> 
class Dozen { 
    public: 
    ArrayInClass<T,12> contents; 
}; 

注意如何在这个例子中,名字T是两个模板参数和 模板参数。因此,可以使用一种机制来构建更简单模板的更复杂的模板。当然, 这与我们的 组装类型和功能的机制没有根本的不同。

我无法理解任何东西。我非常感谢任何帮助我们提供简单而理解的文字。

编辑:

Arrayinclass:

template <typename T, int N> 
class ArrayInClass { 
    public: 
    T array[N]; 
}; 
+0

你不明白哪个特定部分?你了解代码片段吗? –

+0

@Oli,我了解代码,但无法理解粗体段落。 –

+0

你可以缩小你不明白的东西吗?这是一个大而复杂的话题。 – Kevin

回答

7

有在C++中所需要的是特定表达在编译时已知,例如:

int someArray[30]; 

30必须是编译时用C++恒定它可能是:

int someArray[30 + 3]; 

这很好,因为编译器具有编译时需要计算的所有信息,这个数组有多大。但是:

void MyFunc(int numItems) { 
    int someArray[30 + numItems]; 
} 

这是不是编译时间常数,因为用户可致电MyFunc任何整数值。编译器不知道该数组有多大,所以这是一个编译器错误。 (注:C99允许创建任意大小的数组,如C++不允许)。

由于模板参数是一个编译时的值,可以将它传递给需要编译时间值等地方:

template<int ArrayLen> void MyFunc() { 
    int someArray[30 + ArrayLen]; 
} 

这是法律 C++,因为每次使用MyFunc绝指定编译时整数:数组的长度。您不能只拨打MyFunc()您必须致电MyFunc<21>()。由于模板参数必须是编译时可确定的值,因此用户本身无法提供未编译时定义的值。

因为模板参数定义总是编译时,你可以嵌套模板:

template<int ArrayLen> void OtherFunc { 
    MyFunc<ArrayLen + 3>(); 
} 

这个新的模板函数调用旧有阵列3比它被赋予什么大。

+0

最清晰的解释! – Dawson

0

这是说,在你的类模板,Dozen<T>,模板参数T也被用作参数化的成员变量,ArrayInClass<T,N>

所以,如果你实例Dozen<T>为如:

Dozen<float> my_dozen; 

则编译如下会生成代码:

class Dozen<float> { 
    public: 
    ArrayInClass<float,12> contents; 
}; 
+0

ok。告诉我作者意味着什么“因为模板参数最终被编译时值替代,所以它们本身可以用来形成编译时表达式,这在ArrayInClass模板中被用来调整成员数组的大小。必须是一个**所谓的常量表达式**,并且模板参数N就像这样。“ –

+1

@ Mr.Anubis:你还没有发布'ArrayInClass'的代码,所以我不能确定。但我怀疑它有一个像'T data [N];'这样的成员变量。 C++没有可变长度的数组;在编译时必须知道值'N'。 –

+0

谢谢,我粘贴了剩余的代码。 –

0

你大胆已经强调的部分说,当你定义一个类Foo<T>,编译时必须知道T;即它不能是不完整的类型。

对于每个不同类型的T要实例Foo用,编译器创建一个全新的类型(Foo<int>Foo<MyClass>等),这是无任何关系,你可能已经实例化美孚与其他类型的,尽管他们可能都具有与Foo实现所定义的行为相同的行为。

在您发布的示例中,我假设ArrayInClass<T,N>创建了一个T类型的数组,其中包含N元素。

例如,

template< typename T, size_t N > 
class ArrayInClass 
{ 
    T myArr_[N]; 

public: 
    // public interface 
}; 

类中的阵列没有被动态地被声明为静态阵列分配的,尽管它是使用N作为数组长度。这是可能的,因为在实例化ArrayInClass时,编译器将替换您指定的长度作为模板参数。如果编译器无法在编译时确定N,那将不可能。

1

当作者说模板参数最终被编译时常量替代时,他意味着这一点。例如,在下面的非常公知的例子中,所生成的代码是一个数字常数(即,乘法采取在编译时的地方,而不是运行时间:

#include <iostream> 

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

template <> 
struct Factorial<1> 
{ 
    enum { value = 1 }; 
}; 

// example use 
int main() 
{ 
    std::cout << Factorial<5>::value << endl; 
    return 0; 
} 
+0

可能是众所周知的,但我从未见过它。很酷! – Daniel

0

OK,根据您的意见,这里的推理珍闻:

假设你有一个模板:

template <typename T> struct Moo { }; 

现在我们知道,说Moo<Bar>Bar必须是(一个类型的)在编译时已知。到目前为止没有问题。

现在假设我们要建立一些类:

class MyClass 
{ 
    int   a; 
    double  b; 
    Moo<double> m; 
}; 

这也是很好,因为Moo<double>是一个有效的类型。再次,没有问题。但是,现在让我们来概括MyClass的,并使它成为一个模板:

template <typename U> 
class MyClass 
{ 
    int a; 
    U  b; 
    Moo<U> m; 
}; 

现在再次说MyClass<Zoo>Zoo具有在编译时是已知的。因此,依赖类型Moo<U>被替换为MyClass<Zoo>中的Moo<Zoo>,并且因为Zoo已知,所以此成员类型现在也是已知的。

你引用的粗体文本只是说这个推理起作用,你得到了一些有效的东西。

对于typenames来说,这并不令人兴奋,因为所有的类型名称在编译时已知,但模板参数也可以是非类型名称,即整型值。现在在编译时也必须知道这些,并且可以用相同的方式传播它们。

0

我认为你很难找到我们对引用段落所说的更清楚的表达。我认为他们很好地解释了这个问题。

如果您仍然需要帮助把握的想法,试着先了解三个关键字:模板参数参数。这里是我的定义:

一个模板参数是模板定义的一部分,而模板参数是什么获取传递给模板,以便产生具体类型实例化模板。

模板是一个可以进行参数化的功能。在您的例子中,是一个模板:

template <typename T> 
class Dozen 
{ 
... 
}; 

其中牛逼模板参数。不久,T模板参数

也许一个简单的比喻会有所帮助。想象一个模板(这里是Denzen)作为一个雕塑演员,可以填充液体材料,将设置在铸造内部采取其形状,并最终产生雕塑。现在,参数就像液体材料(橡胶,金属,玻璃等))这将给雕塑特定的字符。因此,您可以使用相同的演员制作一系列类似的雕塑。

因此,铸件中的空腔代表T模板参数,您将放置模板参数的占位符,填充模板的位置。

所以,这大致是元编程中的参数化思想。

移动到模板参数与意见的例子:

// T states parameter of Dozen template 
template <typename T> 
class Dozen 
{ 
    // the T is argument used to instantiate concrete type from another template 
    ArrayInClass<T,12> contents; 
}; 

在这里,你可能会认为函数的调用另一个功能和转发参数:

void foo(int a) 
{ 
    bar(a); 
} 

foo不使用自身,但将其作为参数传递给bar。 类似地,Dozen将其自己的模板参数T作为参数ArrayInClass模板来实例化此ArrayInClass的具体类型。

最后,T是编译时表达式。这意味着,它在编译时产生价值。 (表达式是产生一个值的编程语言特性)。表达式的值是一个类型(T)或数字常量()。

ArrayInClass<T,12>也是一个编译时表达式,它产生了一个生成具体类型的ArrayInClass模板的实例。不久之后,编译型表达式可用于构造另一个编译时表达式 - 产生另一个(复杂)值。

在元编程中,最好不要考虑的数字或字符串。类型在这里也是一个值。