为什么需要类型名称,即使似乎足以推断名称应该是一种类型?
Why typename is required even though it seems enough to deduce the name should be a type?
对于以下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{};
}
容易!我们只需创建一个 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();
}
但是,编译器在使用依赖名称解析表达式时,只需处理这些语法假设。依赖宝贝只是一个包含模板参数的名称。
如果您想了解更多关于何时何地放置这些消除歧义的关键字,请阅读以下问题:我必须在哪里以及为什么必须放置"模板"和"类型名"关键字?
相关文章:
- 有没有一种方法可以通过"typedef"为重新定义的基本类型定义特征和强制转换运算符
- 在运行时检查继承是否只有一种类型和 void*
- 文本冒险游戏 - 如何区分一种项目类型与另一种项目类型以及如何构建项目类/子类
- 当 c++ 需要一种数据类型并获取另一种数据类型时会发生什么?
- 在硬件SIMD矢量指针和相应类型之间进行"interpret_cast"是一种未定义的行为吗
- 另一种类型的智能ptr,比如具有弱refs的unique_ptr
- void* 数组将元素转换为另一种类型
- 使用字节向量作为其他类型的原始存储是一种好的做法吗
- 将一种数据类型的向量复制到同一数据类型的结构向量中的有效方法是什么
- 将一种类型的比特重新解释为不同类型的比特的技术
- 有没有一种方法可以使用SFINAE来检测一个类型是否实现了给定的抽象基类
- 在枚举类型上使用std::max是不是一种糟糕的做法
- 为什么需要类型名称,即使似乎足以推断名称应该是一种类型?
- 在 c++ 中将一种结构类型分配给另一种类型
- 将空基类优化对象强制转换为另一种类型是否会破坏严格的别名?
- 我怎样才能让编译器推导出一种类型的 nullptr
- 如何测试指针类型是否可以安全地转换为另一种指针类型?
- 有没有一种很好的方法来实现具有默认失败情况的条件类型?
- 一种类型的多个 cv 分解
- 如何将对象定义为一种类型,然后再将其声明为子类型