为什么需要类型名称,即使似乎足以推断名称应该是一种类型?

Why typename is required even though it seems enough to deduce the name should be a type?

本文关键字:类型 一种 为什么      更新时间:2023-10-16

对于以下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_typeIterator::value_type吗?

我们将玩一个游戏。这是模板游戏。我给你写一个函数模板,你告诉它的作用。准备?去!

这个函数有什么作用?

template<typename T>
void foo() {
T myT{};
}

容易!我们只需创建一个 T 类型的变量!

好!你能告诉我以下内容的作用吗?

template<typename T>
void foo() {
(void) T::thing();
}

我们调用 T 的成员函数名为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();
}

但是,编译器在使用依赖名称解析表达式时,只需处理这些语法假设。依赖宝贝只是一个包含模板参数的名称。

如果您想了解更多关于何时何地放置这些消除歧义的关键字,请阅读以下问题:我必须在哪里以及为什么必须放置"模板"和"类型名"关键字?

相关文章: