4

In this article https://en.wikipedia.org/wiki/Typename, related to use of the keyword Typename, it is written:

template <typename T>
void foo(const T& t)
{
   // declares a pointer to an object of type T::bar
   T::bar * p;
}

struct StructWithBarAsType {
  typedef int bar;
};

int main() {
   StructWithBarAsType x;
   foo(x);
}

The fact that in StructWithBarAsType the dependent bar is in fact a type does not help since foo() could be compiled long before StructWithBarAsType is seen.

I don't understand why foo() could be compiled long before StructWithBarAsType, in which case it could be so?

CinchBlue
  • 6,046
  • 1
  • 27
  • 58
asv
  • 3,332
  • 7
  • 24
  • 47
  • Please read [ask] with a [mcve]. When links die or their content changes the question becomes meaningless for future readers. – Richard Critten Dec 07 '19 at 11:38
  • 1
    ^ super vague comment. What you should do is post the code they mention so if the link ever dies or is changed your question makes sense. That's all :) – Qix - MONICA WAS MISTREATED Dec 07 '19 at 11:39
  • I think maybe my question is different, in substance I ask why the compiler could not see in StructWithBarAsType defintion and solve the ambiguity. Wiki says because foo() could be compiled long before StructWithBarAsType and I ask why this occours. – asv Dec 07 '19 at 15:08
  • You ask *why* compilers don't do it this way, and I think this paragraph from the other question gives you an answer: _This will work and actually is allowed by the Standard as a possible implementation approach. These compilers [...] when an instantiation is needed, they parse the template and possibly detect errors in the definition. But instead of bothering the template's users (poor colleagues!) with errors made by a template's author, other implementations choose to check templates early on and give errors in the definition as soon as possible, before an instantiation even takes place._ – Al.G. Dec 07 '19 at 21:54
  • Thanks for confirming, I'm giving my vote for your question being a duplicate of the other one then. – Al.G. Dec 08 '19 at 10:02
  • The complier could do it in this way: do not compile template, when f(x) has called see the type of x which is StructWithBarAsType so compile the template using the StructWithBarAsType information and then resolve ambiguity without typename. Maybe for technical reasons the template cannot be always parsed after f(x) called. – asv Dec 09 '19 at 10:53

1 Answers1

1

When a compiler parses a C++ file, it usually builds a symbol table of identifier to types. This is important when invoking templates, as the C++ compiler will essentially do a "substitution check" to make sure that "things all work out."

The problem is this ambiguous statement:

T::bar * p;

This can be interpreted as either:

  1. static member variable on type T named bar multiplied by p: ? operator*(? T::bar, ? p)
  2. declaration of a pointer of type T::bar* with name p.

This creates some sort of ambiguity when parsing, and this results in a compiler error:

g++ main.cpp && ./a.out

main.cpp: In function 'void foo(const T&)':
main.cpp:9:13: error: 'p' was not declared in this scope
    9 |    T::bar * p;
      |             ^
main.cpp: In instantiation of 'void foo(const T&) [with T = StructWithBarAsType]':
main.cpp:14:9:   required from here
main.cpp:9:11: error: dependent-name 'T::bar' is parsed as a non-type, but instantiation yields a type
    9 |    T::bar * p;
main.cpp:9:11: note: say 'typename T::bar' if a type is meant

main.cpp:9:11: error: dependent-name 'T::bar' is parsed as a non-type, but instantiation yields a type

In order to clear up this parsing error, C++ has a "syntax modification" to "signal" that an identifier in a template is intended to be a type name and not a variable name. This "syntax modification comes in the form of typename:

struct StructWithBarAsType {
  typedef int bar;
};

template <typename T>
void foo(const T& t)
{
   // declares a pointer to an object of type T::bar
   typename T::bar * p;
}

int main() {
   StructWithBarAsType x;
   foo(x);
}

and this compiles.

Note that similar problems happen related to parsing in C++. See the most vexing parse.

CinchBlue
  • 6,046
  • 1
  • 27
  • 58
  • Ok, but when the compile sees "foo(x)" with x of type StructWithBarAsType could not see in StructWithBarAsType defintion and solve the ambiguity? – asv Dec 07 '19 at 15:02
  • Wiki says because foo() could be compiled long before StructWithBarAsType and I ask why this occours. It is impossible create a compiler to solve that kind of ambiguity? – asv Dec 07 '19 at 15:11
  • Not entirely sure, but I'd imagine the emphasis to be on "parse-time guarantees." By adding `typename`, you equip the template with the `T::Bar is-a type` association that is necessary to help the compiler verify and connect the dots with `StructWithBarAsType::bar, T = StructWithBarAsType in foo(x)`. – CinchBlue Dec 09 '19 at 02:50
  • The complier could do it in this way: do not compile template, when f(x) has called see the type of x which is StructWithBarAsType so compile the template using the StructWithBarAsType information and then resolve ambiguity without typename. Maybe for technical reasons the template cannot be always parsed after f(x) called. – asv Dec 09 '19 at 10:52