对于以下 C++ 代码:
#include <iterator>
template<typename It>
void dwim(It b)
{
typename std::iterator_traits<It>::value_type currValue = *b;
}
我不明白为什么这里需要 typename
。因为std::iterator_traits::value_type , 编译器不应该推断出 value_type
是 Iterator::value_type
吗?
我们来玩个游戏。这是模板游戏。我给你写一个函数模板,你告诉它做什么。准备好?去吧!
这个函数有什么作用?
template<typename T>
void foo() {
T myT{};
}
Easy! We simply create a variable of type T!
很好!你能告诉我以下内容是做什么的吗?
template<typename T>
void foo() {
(void) T::thing();
}
We call a member function of T named thing
?
是的!好的,我将尝试使用具体类型调用该函数:
struct Bar {
using thing = int;
};
foo<Bar>();
你注意到了吗?您认为 T::thing()
会调用一个函数,但现在它只是创建一个 int!看一下实例化函数的样子:
template<>
void foo<Bar>() {
// thing is not a function, but a member type!
(void) Bar::thing();
}
这很糟糕。看起来像访问静态变量和调用函数的东西与访问成员类型或创建成员类型的实例的语法相同!
这可能会导致一些严重的错误。我们不想在想调用函数时实例化类型,也不想在只想比较一些数字时调用模板函数(是的,这种语法也可能有歧义!)。因此,编译器将假定 ::
之后的内容不是成员类型或成员模板,而是普通成员。
如果编译器假定访问的是静态数据成员,但结果访问的是成员类型,则会抛出编译时错误。
但是当你真的想使用一个成员类型时,你可以告诉编译器不要假设你访问一个数据成员,而是假设一个类型:
template<typename T>
void foo() {
// I really want T::thing to be a type!
(void) typename T::thing();
}
但是编译器只需要在解析使用从属名称的表达式时处理这些语法假设。依赖宝贝只是一个包含模板参数的名称。
如果您想了解更多关于在何处以及何时放置这些消除歧义的关键字,请阅读以下问题:Where and why do I have to put the "template" and "typename" keywords?