C++ 为什么我需要在g+;中使用typedef typename+;但不是VS?

C++ 为什么我需要在g+;中使用typedef typename+;但不是VS?,c++,g++,typedef,typename,C++,G++,Typedef,Typename,GCC已经有一段时间没有发现我有这个问题了,但它就发生在今天。但我一直不明白为什么GCC需要模板中的typedef typename,而VS和我猜ICC不需要。typedef typename是一个“bug”还是一个要求过高的标准,还是留给编译器编写人员的东西 对于那些不知道我的意思的人,这里有一个示例: template<typename KEY, typename VALUE> bool find(const std::map<KEY,VALUE>& cont

GCC已经有一段时间没有发现我有这个问题了,但它就发生在今天。但我一直不明白为什么GCC需要模板中的typedef typename,而VS和我猜ICC不需要。typedef typename是一个“bug”还是一个要求过高的标准,还是留给编译器编写人员的东西

对于那些不知道我的意思的人,这里有一个示例:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}
模板
布尔查找(常量标准::映射和容器、常量键和键)
{
std::map::const_迭代器iter=container.find(key);
return iter!=container.end();
}
上述代码在VS中编译(可能在ICC中编译),但在GCC中失败,因为它希望这样:

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typedef typename std::map<KEY,VALUE>::const_iterator iterator; //typedef typename
    iterator iter = container.find(key);
    return iter!=container.end();
}
模板
布尔查找(常量标准::映射和容器、常量键和键)
{
typedef typename std::map::const_迭代器迭代器;//typedef typename
迭代器iter=container.find(key);
return iter!=container.end();
}

注意:这不是我正在使用的实际函数,只是一些愚蠢的东西来说明问题。

好吧,GCC实际上并不需要
typedef
--
typename
就足够了。这项工作:

#include <iostream>
#include <map>

template<typename KEY, typename VALUE>
bool find(const std::map<KEY,VALUE>& container, const KEY& key)
{
    typename std::map<KEY,VALUE>::const_iterator iter = container.find(key);
    return iter!=container.end();
}

int main() {
    std::map<int, int> m;
    m[5] = 10;
    std::cout << find(m, 5) << std::endl;
    std::cout << find(m, 6) << std::endl;
    return 0;
}
#包括
#包括
模板
布尔查找(常量标准::映射和容器、常量键和键)
{
typename std::map::const_迭代器iter=container.find(key);
return iter!=container.end();
}
int main(){
std::map m;
m[5]=10;

std::cout看起来VS/ICC在它认为需要的地方提供了
typename
关键字。注意这是一件坏事(TM)-让编译器决定你想要什么。这会使问题变得复杂化,因为在需要的时候会跳过<代码>类型名称< /C>的坏习惯,这是一个可移植的噩梦。这绝对不是标准行为。尝试严格的标准模式或COMMO。

< P>这是微软C++编译器中的一个bug。d::map::iterator可能不是一个类型(例如,您可以在键、值上使用专门的std::map,以便std::map::iterator是一个变量)


GCC强制您编写正确的代码(即使您的意思很明显),而Microsoft编译器正确地猜测您的意思(即使您编写的代码不正确).

标准要求使用typename。模板编译需要两步验证。在第一步中,编译器必须验证模板语法,而不实际提供类型替换。在这一步中,假定std::map::iterator是一个值。如果它确实表示类型,则需要typename关键字

为什么有必要这样做?在替换实际的键和值类型之前,编译器不能保证模板没有专门化,并且专门化没有将迭代器关键字重新定义为其他内容

您可以使用以下代码进行检查:

class X {};
template <typename T>
struct Test
{
   typedef T value;
};
template <>
struct Test<X>
{
   static int value;
};
int Test<X>::value = 0;
template <typename T>
void f( T const & )
{
   Test<T>::value; // during first pass, Test<T>::value is interpreted as a value
}
int main()
{
  f( 5 );  // compilation error
  X x; f( x ); // compiles fine f: Test<T>::value is an integer
}
类X{};
模板
结构测试
{
T值;
};
模板
结构测试
{
静态int值;
};
int Test::value=0;
模板
空f(T常数&)
{
Test::value;//在第一次传递期间,Test::value被解释为一个值
}
int main()
{
f(5);//编译错误
X;f(X);//编译精细的f:Test::value是一个整数
}

最后一次调用失败,错误指示在f()的第一个模板编译步骤中Test::value被解释为一个值,但是使用类型X实例化测试模板会产生一个类型。

应该注意的是,值/类型匹配问题不是根本问题。主要问题是解析问题。

template<class T>
void f() { (T::x)(1); }
如果您不记得的话,cast的优先级比C中的函数调用高,这也是Bjarne希望使用函数样式cast的原因之一。因此,无法判断上述方法是否意味着

(a)(b)  (c)   // a is a typename

我在其中插入空格以指示分组


注意:同样需要“templatename”关键字,原因与“typename”相同,在C/C++中,如果不知道它们的种类,就不能解析它们。

这里,我认为您指的也是以“typename”开头的行中的键、值有了这个变化,它为我编译。):我看到了你对这个问题的评论,现在就把它固定了:)为了一个好的FAQ,考虑……:迭代器可以引用一个静态成员。,这是强制转换还是函数调用?请注意,优先级也不同!向C添加typedef的人应该被枪毙:)如果编译器故意这么做,那将是一件坏事。事实上,它只是在断开的代码上这样做。实际上,标准中没有禁止编译断开的代码。仍然应该是一个警告(诊断)但是,在g++中需要它的原因是,g++更符合标准。VS在模板化解析的这一部分上有点松懈(这在更复杂的模板中会导致其他问题)是的,但是为什么标准的friggin会这样做呢?我处理过相同的代码!我想你把对
f
f(X());
成功而
f(5)的两个调用的评论弄混了;
是一个编译错误。无论如何,MSVC处理得很好——它似乎会延迟决定
Test::value
是一个值还是一个类型,直到模板被实例化。但是,它不会对类模板的成员这样做。@Sumudu:你是对的,我还更正了
f(X())
调用上面更显式的代码。如果MSVC延迟检查直到类型被实例化,那么MSVC不符合标准。实际上,在决定之前,MSVC似乎会检查std::map::iterator是否是一个类型。我没有标准的副本,但这似乎是不符合标准的行为,但它会继续ly意味着它将(尝试)纠正并编译一些不正确的程序,而不是在正确的程序中引入错误。是的,这是一个错误,因为编译器不会对非法代码发出诊断。没有非法代码。诊断
(a)(b)  (c)   // a is a typename
(a) (b)(c)    // a is not a typename , b is
(a)(b) (c)    // neither a nor b is a typename