Should the name of a function template be visible during lookup of a name preceding ::?
Asked Answered
P

2

9

Both clang and gcc reject this code:

template<int i>
struct ambiguous
{
    static const int value = i;
};

namespace N
{
    template<int i>
    void ambiguous();

    int i = ambiguous<3>::value; // finds the function template name
}

However, they both accept the following code:

struct ambiguous
{
    static const int value = 0;
};

namespace N
{
    void ambiguous();

    int i = ambiguous::value;
}

The standard says that name lookup of a name preceding :: "considers only namespaces, types, and templates whose specializations are types". Are clang and gcc correct in rejecting this code? If so, what am I missing?

From C++ Working Draft Standard n3337

3.4.3 Qualified name lookup [basic.lookup.qual]

The name of a class or namespace member or enumerator can be referred to after the :: scope resolution operator (5.1) applied to a nested-name-specifier that denotes its class, namespace, or enumeration. If a :: scope resolution operator in a nested-name-specifier is not preceded by a decltype-specifier, lookup of the name preceding that :: considers only namespaces, types, and templates whose specializations are types. If the name found does not designate a namespace or a class, enumeration, or dependent type, the program is ill-formed.

14.2 Names of template specializations [temp.names]

For a template-name to be explicitly qualified by the template arguments, the name must be known to refer to a template.

After name lookup (3.4) finds that a name is a template-name or that an operator-function-id or a literal-operator-id refers to a set of overloaded functions any member of which is a function template if this is followed by a <, the < is always taken as the delimiter of a template-argument-list and never as the less-than operator.

Edit

To avoid confusion of this issue with the ambiguity between an expression and a declaration, here is the original code with the templates using a type parameter instead of a non-type parameter.

template<class>
struct ambiguous
{
    static const int value = 0;
};

namespace N
{
    template<class>
    void ambiguous();

    int i = ambiguous<int>::value; // finds the function template name
}

This results in the same error in all cases. The < cannot be interpreted as an operator.

ambiguous is unambiguously a template-name, but could either be a type or a function. It's possible to parse the entire template-id without knowing whether it names a function or a type, and resolve the ambiguity later on. Does the standard excuse the implementor from doing this?

Paestum answered 19/8, 2013 at 10:24 Comment(10)
msvc and ICC also reject it with the same error.Paestum
OK, so what is the error message?Crippen
clang: error: qualified name refers into a specialization of function template 'ambiguous', gcc: cannot resolve overloaded function ‘ambiguous’ based on conversion to type ‘int’, msvc 'ambiguous' : use of class template requires template argument listPaestum
This particular ambiguity is one of the things that makes C++ very difficult to parse. We don't know until :: whether or not we should ignore the non-type name - so the ambiguity remains unresolved until we parse the entire template-id :D.Paestum
@Paestum Yes. And we can't parse a template-id until we know that ambiguous names a template, which means that the name has to be bound first. (The < could be a less than.)Oxblood
@Paestum Re your edit: it's only valid if the token immediately following the < is a keyword which names a type (like int); it ceases to be valid for a user defined type (since the lookup used will depend on whether < is less than, or opens a template argument list). Presumably, the committee didn't want to make this a special case.Oxblood
@JamesKanze Agreed, we can only parse unambiguously if we allow speculatively parsing a template-id (that begins with an identifier that is unambiguously a template-name but may be either be a function or type name) without binding the name. Does the standard prohibit this?Paestum
@JamesKanze: What makes the lookup of the identifier after < differ between an expression or a template-argument-list? Even if the name of the template is a qualified-id, "the names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs."Paestum
@Paestum The standard tries to avoid cases which would require backtracking. Not only do they make the compiler more complex, they make the code much more difficult to understand for a human reader.Oxblood
@Paestum The lookup won't necessarily differ for the first token, but the way what follows is parsed definitely will.Oxblood
O
8

The problem is that the paragraph you quote ends up being applied too late. Before getting there, the compiler must determine that in ambiguous<3>::value, the < and > are template argument delimiters, and not greater than and less than. (Consider:

int ambiguous;
int value:
//  ...
int i = ambiguous<3>::value;

, which parses as (ambiguous < 3) > ::value, where the < and > are less than and greater than, respectively.) This involves a lookup of ambiguous as an unqualified name, and binds the symbol to N::ambiguous. Afterwards, you're stuck with the instantiated template N::ambiguous<3> to the left of ::, which isn't legal.

EDIT:

This issue isn't as clear as one might like: the standard only refers to section 3.4 in section 14.2, where it discusses this, and section 3.4 discusses all of the possible rules of name lookup. On the other hand, there is really only one way to interpret it: the compiler cannot parse anything further until it knows whether ambiguous names a template or not, and can decide whether the following < is greater than, or opens a template argument list. And of course, it cannot "rebind" the argument later, once it has parsed the following tokens, since in the general case, rebinding could change the meaning of the <, invalidating the parse. In practice, although the standard doesn't say so as clearly as it probably should, the name lookup in this case must be unqualified name lookup (or class member access, if the name is preceded by a . or a -> operator).

Oxblood answered 19/8, 2013 at 10:40 Comment(12)
In my example, ambiguous is unambiguously a template-name, but could either be a type or a function. It's possible to parse the entire template-id without knowing whether it names a function or a type, and resolve the ambiguity later on. Does the standard excuse the implementor from doing this?Paestum
@Paestum The standard is very string concerning name lookup, and doesn't allow the implementor any liberty in this regard. (Or if it does, and there is no undefined behavior, then it is a serious defect in the standard.)Oxblood
@Paestum And in your example, when unqualified name lookup is used, N::ambiguous hides ::ambiguous. (A more interesting case would be if you defined both in the same scope. In this case, in general, struct ambiguous would refer to the struct, and other uses to the function, but the text you cited could also be interpreted that in ambiguous<3>::, the compiler should find the struct, and not the function.Oxblood
It's possible to parse your example unambiguously if we defer the binding of ambiguous to N::ambiguous until reaching ::, at which point the ambiguity can be resolved. There is no type named ambiguous, so the :: cannot be part of a nested-name-specifier ::, therefore ambiguous<3>::value can only be interpreted as an expression.Paestum
It's possible to parse any of the example if we allow backtracking and speculative parsing. The case above is one of the worst, since when backtracking, we have to actually change some of the tokens.Oxblood
The C++ grammar has a number of constructs that cannot be parsed without unlimited backtracking ;)Paestum
@Paestum I'm only aware of one, the most vexing parse. Which is only present because we started from C's declaration syntax. Anyway: the standard says what it says. If you don't like it, you should complain to the committee, not me. But don't expect much sympathy. The committee would get rid of the most vexing parse if it could, and would definitely prefer a grammar with no ambiguities, and which could be parsed directly by recursive descent.Oxblood
I'm not complaining - on the contrary I take some degree of masochistic pleasure in the challenge posed by the grammar ;). I'm just trying to confirm whether or not the standard intends that my example should be accepted by an implementation or not.Paestum
Would love to know template name hiding rules!?Speck
In this case, I'm more than sure about the intent. I was present when Andy Koenig first spotted the ambiguity with regards to < in this case, and the problems it could cause in parsing. Parsing templates is difficult enough as is; I can assure you that nobody in the committee at that time wanted to make it any harder.Oxblood
@DieterLücking Template name resolution is discussed in section 14.6. Name lookup is basically the same as in non-templates, once you've established whether the name is dependent or not, which determines in what context the lookup takes place, and what is visible in that context.Oxblood
@JamesKanze Actually i was reading it without any comprehension.Speck
S
3

Seem templates in the inner namespace hide the names of the outer.

template<int i>
struct A
{
    static const int value = 0;
};

struct B
{
    static const int value = 0;
};

typedef A<0> C;


namespace N
{
    // The local function A is not declaced, yet, but the global A is:
    int early_e = A<3>::value; // Ok: The A in the global namespace. [simple-template-id}

    template<int i>
    int A() { return 0; }
    int B() { return 0; }
    int C() { return 0; }

    int a = A<3>();        // Ok: The A in the namespace. [simple-template-id}
    int b = N::A<3>();     // Ok: The A in the namespace. [N::simple-template-id]
    int c = ::N::A<3>();   // Ok: The A in the namespace. [::N::simple-template-id]
    int d = ::A<3>::value; // Ok: The A in the global namespace. ::simple-template-id::identifier]
    // The local function A is no type: "templates whose specializations are types"
    int e = A<3>::value;   // Error: The namespace has the function name A,
                           // which hides the global A. [simple-template-id::identifier]
    // The local function B is no type, but the global B is a type:
    int f = B::value;      // Ok: The B in the global namespace. [class-name::identifier]
    // The local function C is no type, but the global typedef C is a type:
    int g = C::value;      // Ok: The C in the global namespace. [typedef-name::identifier]
}

int main() {
    return 0;
}
Speck answered 19/8, 2013 at 10:43 Comment(3)
Yes. That's exactly what he was asking about.Oxblood
That's a comprehensive analysis of the behaviour.. but of which compiler? This highlights the inconsistency: typedef A<0> C followed by C::value is allowed, but A<3>::value is rejected.Paestum
@Paestum In the global namespace C is a typedef, A a template only. Hence, hiding A in the local namespece, makes it a function, not a type. In case of C the type in the outer namespace is considerd.Speck

© 2022 - 2024 — McMap. All rights reserved.