4
struct A{};

template <typename T>
struct B
{
    typename ::A a1; //(1)
    typename A a2; //(2): error
};

int main(){return 0;}

Why is the first case correct, but the second isn't? I don't understand the meaning of that restriction.
And anyway, why is the first case allowed? ::A isn't template-parameter dependent name. What's meaning in it?

Denis
  • 2,786
  • 1
  • 14
  • 29
  • Your example works fine: http://rextester.com/BEF67104. Show the original code – grisha Jan 06 '15 at 12:22
  • 5
    @user2451677 fails to compile in both g++ and clang++ – vsoftco Jan 06 '15 at 12:22
  • I really hope for a really good answer on this question. I am intrigued by this. I am not delusional to think that I know everything in C++ but I think at least I know what I know and what not but there is always someone showing me that I don't know squat. – bolov Jan 06 '15 at 12:27
  • I know, that I need to use something else, but not VC++ :). It has always less restrictions – grisha Jan 06 '15 at 12:30
  • 1
    @user2451677 the word you are searching for is non-conforming :) Although to be fair in the last years significant efforts have been made to bring VC to the C++14 standard. – bolov Jan 06 '15 at 12:32
  • @bolov: significant efforts have been made to bring VC to the C++11 standard* – Lightness Races in Orbit Jan 06 '15 at 12:51
  • 1
    @LightnessRacesinOrbit Herb Sutter said that VC aims directly at C++14, implementing C++14 features like generic lambdas before many C++11 features are implemented. My note: They don't have an option for specifying the C++ dialect, so I guess that makes sense for them. – bolov Jan 06 '15 at 12:58
  • @bolov: It was a joke, based on VS not even fully supporting C++11 yet (and having quite some way to go). What Sutter says backs that up. It's kinda crazy. – Lightness Races in Orbit Jan 06 '15 at 13:07
  • @LightnessRacesinOrbit I did say efforts, but yeah I completely missed your irony. Need to upgrade my antennas. – bolov Jan 06 '15 at 13:09

2 Answers2

6

The rule isn't that you can only use typename if the type is nested in a dependent scope. The rules are, more or less:

  • you must use typename if it's in a dependent scope
  • you can only use typename where it's allowed by the grammar.

The grammar allows it for a subset of qualified-id, specified by

typename-specifier:
    typename nested-name-specifier identifier
    typename nested-name-specifier template<opt> simple-template-id

nested-name-specifier:
    :: (C++14 or later)
    ::<opt> type-name ::
    ::<opt> namespace-name ::
    decltype-specifier ::
    nested-name-specifier identifier ::
    nested-name-specifier template<opt> simple-template-id ::

So the second case is certainly forbidden, since it doesn't involve any nesting. Strictly speaking, before C++14, the first was also forbidden, since a global qualifier :: didn't match that grammar.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
5

As @MikeSeymour's answer explains, going strictly by the standard (C++11, I don't have a C++14 text on hand), case (1) should actually be erroneous as well - typename prefixing a qualified name can only be used when there's at least one name on the left-hand side of the ::.

However, as pointed out by @hvd in the comments, CWG issue 382 indicates the actual intent is to allow typename before any qualified name, including global-namespace qualification. Since this is what most compilers seem to implement, the rest of this answer follows with this idea.

When viewed like this, it's not that case (2) is a restriction, it's that case (1) is benevolence. The required rule (and its very original wording, I believe) is basically that "if a qualified name which depends on template parameters denotes a type, you must prefix it with typename." For convenience, it is loosened to "typename can be used for any qualified name which dentoes a type, whether it's dependent or not."(1)

In contrast, an unqualified name can never be ambiguous as to whether it refers to a type or not, so it never requires typename and typename is therefore not allowed there.


(1)This loosening is not really explicitly stated in the standard, but follows from the combination of several rules. Basically, wherever a simple-type-specifier (something which denotes a type) is allowed, the grammar also allows a typename-specifier (a qualified name prefixed with typename). The rules for templates (mainly in 14.6) only state that typename is required when a dependent qualified name denotes a type. Since nothing forbids typename in other contexts, it can be used with any qualified name which denotes a type (even outside of template context).

Community
  • 1
  • 1
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • I'm trying to find the exact wording of the convenience loosening in the standard; I will add it to the answer once I succeed. – Angew is no longer proud of SO Jan 06 '15 at 12:31
  • 1
    There isn't even any requirement that `typename` only be used inside templates. `struct S { }; typename ::S s;` is perfectly valid. –  Jan 06 '15 at 12:37
  • 3
    @bolov: `::A` isn't ambiguous; but some qualified-ids are, and the grammar just specifies (more or less) that you can use `typename` with any qualified-id, regardless of whether it's necessary to avoid ambiguity. An unqualified-id can't be ambiguous, and the grammar doesn't allow the use of `typename` with one of those. – Mike Seymour Jan 06 '15 at 12:43
  • 2
    Although by my reading of the grammar, `typename ::A` isn't valid. A *typename-specifier* doesn't allow any *qualified-id*, just those involving a *nested-name-specifier*, which involves nesting inside a named type or namespace. `::` alone isn't one of those. – Mike Seymour Jan 06 '15 at 12:48
  • @MikeSeymour Heck, you're right. Seems the answer's ready for a re-write from the ground up. Or, actually, you should write an answer and I'll just delete mine. – Angew is no longer proud of SO Jan 06 '15 at 12:50
  • @MikeSeymour Good point. It has been explicitly stated that the intent is that all qualified names can be preceded by `typename` (CWG issue 382) though, and that is what compilers implement, so this is almost surely an oversight. –  Jan 06 '15 at 12:53
  • 1
    @hvd: Actually, my comment is out of date. C++14 seems to have added `::` as a *nested-name-specifier* (referring to [this draft](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf)). – Mike Seymour Jan 06 '15 at 13:01