13

Why is that case incorrect (it's logical)

template <typename T>
struct Der: public Base
{
    typedef int T;
    T val;
};

, but that case is correct?

struct Base
{
    typedef int T;
};

template <typename T>
struct Der: public Base
{
    T val;
};

The Standard 14.6.1/7 says:

In the definition of a class template or in the definition of a member of such a template that appears outside of the template definition, for each base class which does not depend on a template-parameter (14.6.2), if the name of the base class or the name of a member of the base class is the same as the name of a template-parameter, the base class name or member name hides the template-parameter name (3.3.7).

Why is it not ambiguous in here?

Columbo
  • 60,038
  • 8
  • 155
  • 203
Denis
  • 2,786
  • 1
  • 14
  • 29

3 Answers3

15

The first example is incorrect according to [temp.local]/6:

A template-parameter shall not be redeclared within its scope (including nested scopes).

However, in

template <typename T>
struct Der: public Base
{
    T val;
};

T is hidden by the name inherited from Base - as specified by your quote.

[..] if the name of the base class or the name of a member of the base class is the same as the name of a template-parameter, the base class name or member name hides the template-parameter name (3.3.7).

That is, the member val is of type int. Demo.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • Thanks for your answer, but what are the reasons for such behavior? – Denis Jan 19 '15 at 17:02
  • 1
    @user3514538 Redeclaring a template-parameter would probably lead to problems with [basic.scope.class]/1 - an important paragraph. Not to mention the ODR. However, concerning the second bit, I'm not sure. We should wait for hvd :o) – Columbo Jan 19 '15 at 17:05
  • 1
    This seems to be subject of [CWG 591](http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#591), though that only restricts this rule to non-dependent base classes. – dyp Jan 19 '15 at 18:49
  • 1
    And the whole thing is subject of [CWG 459](http://open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#459). – dyp Jan 19 '15 at 18:52
  • @dyp I hoped that someone had written a CWG issue. It didn't really make sense to me either. – Columbo Jan 19 '15 at 19:17
  • Well, as it's said in the discussion, it seems to "fall straightforwardly out of the lookup rules". That is, lookup rules for template parameters are a bit weird, their "scope" is compared to namespace scope in the discussion of the issue (but IMHO that's unintuitive). – dyp Jan 19 '15 at 19:40
3

In general, the standard tries to ensure that the meaning of a type in a given scope is the same.

If we pretend that the typedef is allowed then consider the types of val1, val2, and val3 in the following?

template <typename T>
struct Der: public Base
{
    void f1() {
      T val1;      // What is the type of 'val1'?
    }

    T val2;        // What is the type of 'val2'?

    typedef int T;

    T val3;        // What is the type of 'val3'?
};

The only variable to have the template parameter type T would be 'val2'.

This is because the standard requires that all members of the class be "in scope" for f1. This is why member functions can refer to member variables defined later in the class body.

The same problem can be demonstrated with typedefs too:

typedef int T;

struct S
{
  T x;
  typedef float T;
  T y;
};

Again, if this was legal then the T used for the declaration of x would refer to ::T and the T used for y would refer to typedef S::T.

This is covered by the standard under ISO 3.3.7/1:

The following rules describe the scope of names declared in classes.

...

2) A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

Richard Corden
  • 21,389
  • 8
  • 58
  • 85
1

Because the second is unambigous:

You might not care or know that there was the typedev int T in the superclass, but you've just introduced T as template parameter, which makes it clear that you're referring to it when using T in Der.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
  • 3
    The `T` in derived is actually the member of the base, `Base::T`, in the second example. This would appear to contradict your answer - or am I missing something? – Richard Corden Jan 19 '15 at 17:05
  • I don't think this answer is _wrong_ per se, but it doesn't really explain anything either. – Lightness Races in Orbit Jan 19 '15 at 17:51
  • @LightnessRacesinOrbit `T` refers to the base class typedef. The answer says *"you've just introduced `T` as template parameter, which makes it clear that you're referring to it when using `T` in Der."* – Columbo Jan 19 '15 at 18:43
  • @Columbo: That's no more true than it is in the first case. This answer doesn't explain why the template parameter `T` should magically gain precedence just because the other `T` is inherited from a base (despite this fact being true). – Lightness Races in Orbit Jan 20 '15 at 13:48
  • @LightnessRacesinOrbit: Um, I tried to make a point of the coder expressing his explicit desire to introduce the name 'T', which makes the "right" behaviour "obvious". – Marcus Müller Jan 20 '15 at 13:58
  • @MarcusMüller: The code explicitly introduced both names 'T', in both cases. There's nothing "obvious" about which one should take precedence! Nope, not even when one is lexically closer. – Lightness Races in Orbit Jan 20 '15 at 14:44