为什么在VS2015中,模板相关的嵌套类型名称不需要使用typename关键字?

3
我在阅读有关C++模板编程中typename的用法(如此问答)时,发现当使用依赖嵌套类型名称时,应该使用typename来避免解析歧义。我还在Scott Meyers的书《Effective C++》的第42项中检查了这一点。
但是让我感到奇怪的是,书中相同的示例在没有typename的情况下也能正常工作。以下是代码:
template<class C>
void Print2nd(const C & cont)
{
   if (cont.size() >= 2)
   {
      C::const_iterator * iter1 = new C::const_iterator(cont.begin());  // why typename is NOT needed?
      C::const_iterator   iter2 = cont.begin();                         // why typename is NOT needed?
      (*iter1)++;
      iter2++;
      int value1 = **iter1;
      int value2 = *iter2;

      std::cout << "The value of 2nd with pointer is: " << value1 << std::endl;
      std::cout << "The value of 2nd without pointer is: " << value2 << std::endl;
   }
}


int main()
{
   std::vector<int> vect = {1,2,3,4,5,6};
   Print2nd(vect);
   return 0;
}

我正在使用VS2015。那么,问题是为什么在这种情况下不需要使用typename?最近的C++编译器有任何升级可以避免在这种情况下使用typename吗?或者我在代码中犯了一个错误?
更新1:感谢@FrançoisAndrieux的评论,据此问答报告,在VS2008和VS2010中也发生了同样的情况。

1
后来的编译器确实会提出警告:https://godbolt.org/z/vw3bsQ 你检查过警告级别了吗? - Richard Critten
有趣的是,大约两年时间后,这应该会很完善。 - StoryTeller - Unslander Monica
可能是编译器有问题 - 你尝试过使用其他的编译器进行测试吗? - Toby Speight
@TobySpeight 不是的,我刚在VS2015中检查过了。 - TonySalimi
@Gupta 这是 Visual Studio 的一个不符合规范的问题。他们已经有这个 bug 很长很长时间了。 - François Andrieux
类似的问题,但是针对于VS2008和VS2010。 - François Andrieux
2个回答

5
中,不需要使用typename。 在某些情况下,由于语法上的限制,不需要typename,因为那里的任何东西必须是一个类型。
特别是:

在type-id中出现的限定名称,其中最小的封闭type-id是:

  • 新表达式中的类型,该类型不会将其类型括在括号中;
引用来源并非直接来自标准,但相当可靠。
之前,这里需要使用 typename,否则它会被解析为值,而 new value 不是有效的语法。在 中,在该上下文中,typename 是可选的。
现在,中没有的功能;你看到的是MSVC未能正确实现//,而不是扩展。

1
所以它原来是个漏洞,现在变成了一个功能? ;) - NathanOliver
@NathanOliver 这个 bug 更像是“编译器忽略了从未被调用的函数中的语法错误,因为这个特定的编译器在链接时进行编译”。而这个特性则更像是“这个函数中的某些东西不是语法错误”。它们都使得某些代码不会破坏编译,但是这个 bug 恰好覆盖了这个特性,有点偶然的意味。 - Yakk - Adam Nevraumont

4
需要 typename 关键字;这个例子程序是不合法的。如果编译器不能发现问题,那么它就不符合标准。一个正确的版本如下所示:
typename C::const_iterator * iter1 = new typename C::const_iterator(cont.begin());
                                      // ^^^^^^^^ this one only required until C++20
typename C::const_iterator   iter2 = cont.begin();

标准引用(C++17草案):

[temp.res]

在模板声明或定义中使用的名称,该名称依赖于模板参数且未被视为类型名,除非适用的名称查找找到类型名称或该名称由关键字typename限定。

C::const_iterator 依赖于模板参数 C,因此不应假定它是类型名,除非使用typename。我认为这个语句应该被解释为乘法操作,但右操作数是未声明的标识符,因此不合法。

C++20引入了一条规则,允许从new表达式中删除typename(最新草案):

[temp.res]

如果限定名称出现在类型标识符、new-type-id或定义类型标识符中,并且最小的封闭类型标识符、new-type-id或定义类型标识符是new-type-id、定义类型标识符、trailing-return-type、模板的类型参数的默认参数或static_­cast、const_­cast、reinterpret_­cast或dynamic_­cast的type-id,则称限定名称处于仅类型ID上下文中。


您可能需要为可读性创建一个类型别名:

using const_iterator = typename C::const_iterator;

您也可以使用auto

auto it = cont.begin();

顺便说一句,动态分配迭代器几乎没有什么意义。


VS因在这些情况下过于宽容而臭名昭著,允许省略typenametemplate - François Andrieux
你能提供这个案例的标准参考吗? - TonySalimi
@Gupta添加了标准引用。 - eerorika
@Yakk-AdamNevraumont 基类和成员初始化列表是两个例外,你绝对不应该在那里使用 typename - TonySalimi
但标准规定:“除非适用的名称查找找到一个类型名称或...”。因此,如果MSVC类型名称查找机制正确地找到了类型名称,那么它就不会违反标准,对吗? - TonySalimi
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接