在下面的代码中,为什么我必须写someClass<Item>::increment()
而不是仅仅写someClass::increment()
?
编译器本身是否不能意识到someClass
是采用<typename Item>
的模板类?
template <typename Item>
struct someClass {
void increment();
Item x;
};
template <typename Item>
void someClass<Item> :: increment() {
x++;
}
答案 0 :(得分:2)
由于someClass
是模板类,因此它取决于参数,方法和属性也取决于该模板参数:
template <typename Item>
void someClass<Item> :: increment() {
x++;
}
对于每个不同的Item
,increment()
都有不同的代码体。可能someClass
的布局取决于Item
,使x++
指向this
内部的另一个位置。
在此特定示例中,Item
从未直接使用过,因此该语言可以隐式使用它。可以争辩说,代码可以做得短一些。用CTAD完成,这要困难得多。出于意见考虑,在极少数情况下可以节省几行代码,是否值得使该语言复杂化并添加一个令人惊讶的因素。至少在CTAD上有好处。许多程序员会惊讶地发现,看起来像常规类的东西实际上是模板类。
C ++中已经包含了许多惊喜,因此可以说再有一个惊喜并不重要。一切都归结为一个问题,而不是此示例中需要显式(和冗余)模板参数的深层技术原因。因此,政策反对意见,偏爱事实,因此请不要更深入。
答案 1 :(得分:1)
编译器本身不应该意识到
someClass
是一个 模板类需要<typename Item>
?
不一定。这是一个例子。在评论中提到:请考虑someClass
有一个专长:
// general template
template <typename Item>
struct someClass {
void increment();
Item x;
};
// specialization for std::vector<...> parameters
template <typename Item>
struct someClass<std::vector<Item>> {
void increment();
Item defaultItem;
};
现在,定义increment
函数模板如下:
// general
template <typename Item>
void someClass<Item>::increment() {
x++;
}
// specialization
template <typename Item>
void someClass<std::vector<Item>>::increment() {
defaultItem++;
}
如您所见,这两个定义具有相同的template <typename Item>
,但在someClass<...>
上却有所不同。
答案 2 :(得分:0)
编译器本身是否不能意识到
someClass
是采用<typename Item>
的模板类?
当编译器看到::
时,它尚不知道您实际上是在做什么。它所看到的只是该令牌左侧的内容。因此,它会看到一个模板头,后跟一个类型名(void
),然后是一个类模板的名称,然后是作用域运算符(::
)。就是这样。
模板并不是C ++中真正存在的东西。它们是用于创建事物的编译时机制:C ++ 14中的类,函数和变量。模板名称为模板命名,但是名称后跟其模板参数才是实际创建模板要创建的东西的名称。 someClass
为类模板命名; someClass<Item>
命名一个类,而不是一个类模板。
并且类名后可以跟::
。
您基本上要的是让编译器查看模板头,看看给您的模板参数名称与给模板的模板参数名称相同{ {1}},然后自动替换其中。这不是C ++的工作方式。您所要求的与期望编译器填写函数参数并没有太大区别,只是因为调用站点上的变量名称恰好与被调用函数中的参数名称重合。
参数名称,无论是函数还是模板,无关紧要在C ++中。它们只是标识符。它们仅在声明这些名称的范围内起作用。函数参数名称仅在所定义的函数内起作用。模板参数名称仅在相关模板标题的范围内起作用。
您的课程使用与成员定义不同的模板头。因此,就C ++而言,名称无关紧要。这样做是完全有效的:
someClass
您想要的不是C ++的工作方式。是的,C ++可以根据使用的上下文来推导完整的模板名称。但是这些不仅仅基于参数名称;它们基于在呼叫站点传递的参数。