c++ - 为什么 typename 是必需的,即使它似乎足以推断名称应该是一种类型?

标签 c++

<分区>

对于以下 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{};
}

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?

关于c++ - 为什么 typename 是必需的,即使它似乎足以推断名称应该是一种类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45559990/

相关文章:

模板参数上的 C++ 函数模板重载

c# - 如何将 wchar_t* 从 C++ 编码到 C# 作为输出参数或返回值?

c++ - std::reduce 对于 float 有多安全?

c++ - 关于默认捕获模式和 C++ lambda 表达式中的 'this' 的限制

c++ - 这是我在 XCode 中将 boost/filesystem 加入我的 C++ 程序的最后一次尝试

c++ - 您可以在同一对象中拥有对成员 std::shared_ptr<variable> 的成员引用吗?

c++ - 在主循环外绘制背景时闪烁

c++ - 在 C API 中定义了一个类型,如何将它与命名空间中的 C++ 类相关联?

c++ - 如何计算16bit到8bit的量化误差?

c++ - 使用 Visual Studio 2015、C++ 项目的 DLL 没有 "Copy to Output Directory"