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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/642229/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:26:52  来源:igfitidea点击:

Why do I need to use typedef typename in g++ but not VS?

c++g++typedeftypename

提问by Robert Gould

It had been a while since GCC caught me with this one, but it just happened today. But I've never understood why GCC requires typedef typename within templates, while VS and I guess ICC don't. Is the typedef typename thing a "bug" or an overstrict standard, or something that is left up to the compiler writers?

自从 GCC 用这个发现我已经有一段时间了,但它今天才发生。但我一直不明白为什么 GCC 需要在模板中使用 typedef typename,而 VS 和我猜 ICC 不需要。typedef typename 是“错误”还是过于严格的标准,还是留给编译器编写者的东西?

For those who don't know what I mean here is a sample:

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

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();
}

The above code compiles in VS (and probably in ICC), but fails in GCC because it wants it like this:

上面的代码在 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();
}

Note: This is not an actual function I'm using, but just something silly that demonstrates the problem.

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

回答by David Rodríguez - dribeas

The typename is required by the standard. Template compilation requires a two step verification. During the first pass the compiler must verify the template syntax without actually supplying the type substitutions. In this step, std::map::iterator is assumed to be a value. If it does denote a type, the typename keyword is required.

标准需要类型名。模板编译需要两步验证。在第一遍期间,编译器必须在不实际提供类型替换的情况下验证模板语法。在这一步中, std::map::iterator 被假定为一个值。如果它确实表示类型,则需要 typename 关键字。

Why is this necessary? Before substituing the actual KEY and VALUE types, the compiler cannot guarantee that the template is not specialized and that the specialization is not redefining the iteratorkeyword as something else.

为什么这是必要的?在替换实际的 KEY 和 VALUE 类型之前,编译器不能保证模板不是特化的,并且特化不会将迭代器关键字重新定义为其他东西。

You can check it with this code:

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

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
}

The last call fails with an error indicating that during the first template compilation step of f() Test::value was interpreted as a value but instantiation of the Test<> template with the type X yields a type.

最后一次调用失败并显示一个错误,表明在 f() Test::value 的第一个模板编译步骤期间被解释为一个值,但使用 X 类型的 Test<> 模板的实例化产生了一个类型。

回答by Magnus Hoff

Well, GCC doesn't actually requirethe typedef-- typenameis sufficient. This works:

好了,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;
}

This is an example of a context sensitive parsing problem. What the line in question means is not apparent from the syntax in this function only -- you need to know whether std::map<KEY,VALUE>::const_iteratoris a type or not.

这是上下文敏感解析问题的示例。仅从该函数的语法中看不到有问题的行的含义——您需要知道是否std::map<KEY,VALUE>::const_iterator是类型。

Now, I can't seem to think of an example of what ...::const_iteratormight be except a type, that would also not be an error. So I guess the compiler can find out that it hasto be a type, but it might be difficult for the poor compiler (writers).

现在,我似乎想不出一个例子来说明......::const_iterator除了类型之外可能是什么,这也不是错误。所以我猜编译器可以发现它必须是一个类型,但对于糟糕的编译器(作家)来说可能很难。

The standard requires the use of typenamehere, according to litb by section 14.6/3 of the standard.

该标准要求使用typename这里,根据 litb 由标准的第 14.6/3 节。

回答by dirkgently

It looks like VS/ICC supplies the typenamekeyword wherever it thinksit is required. Note this is a Bad Thing (TM) -- to let the compiler decide what youwant. This further complicates the issue by instilling the bad habit of skipping the typenamewhen required and is a portability nightmare. This is definitely not the standard behavior. Try in strict standard mode or Comeau.

看起来 VS/ICC 会typename在它认为需要的任何地方提供关键字。请注意,这是一件坏事 (TM) —— 让编译器决定想要什么。这通过灌输typename在需要时跳过的坏习惯使问题进一步复杂化,并且是可移植性的噩梦。这绝对不是标准行为。尝试在严格的标准模式或Comeau。

回答by JoeG

This is a bug in the Microsoft C++ compiler - in your example, std::map::iterator might not be a type (you could have specialised std::map on KEY,VALUE so that std::map::iterator was a variable for example).

这是 Microsoft C++ 编译器中的一个错误 - 在您的示例中, std::map::iterator 可能不是一种类型(您可以在 KEY,VALUE 上使用专门的 std::map 以便 std::map::iterator 是一个例如变量)。

GCC forces you to write correct code (even though what you meant was obvious), whereas the Microsoft compiler correctly guesses what you meant (even though the code you wrote was incorrect).

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

回答by Yttrill

It should be noted that the value/type kinding issue is not the fundamental problem. The primary issue is parsing. Consider

应该注意的是,值/类型分类问题不是根本问题。主要问题是解析. 考虑

template<class T>
void f() { (T::x)(1); }

There is no way to tell if this is a cast or a function call unless the typename keyword is mandatory. In that case, the above code contains a function call. In general the choice cannot be delayed without forgoing parsing altogether, just consider fragment

除非 typename 关键字是必需的,否则无法判断这是强制转换还是函数调用。在这种情况下,上面的代码包含一个函数调用。一般来说,在不完全放弃解析的情况下不能延迟选择,只考虑片段

(a)(b)(c)

In case you didn't remember, cast has a higher precedence than function call in C, one reason Bjarne wanted function style casts. It is therefore not possible to tell if the above means

如果您不记得了,cast 的优先级高于 C 中的函数调用,这也是 Bjarne 想要函数风格强制转换的原因之一。因此无法判断上述是否意味着

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

or

或者

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

or

或者

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

where I inserted space to indicate grouping.

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

Note also "templatename" keyword is required for the same reason as "typename", you can't parse things without knowing their kind in C/C++.

另请注意,出于与“typename”相同的原因,需要“templatename”关键字,您无法在不知道 C/C++ 中的类型的情况下解析事物。