Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[temp.arg.explicit] p5 Explicitly specified template arguments can enforce to use the template #5341

Closed
xmh0511 opened this issue Mar 15, 2022 · 13 comments

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Mar 15, 2022

[temp.arg.explicit] p5 just states:

[Note 2: An empty template argument list can be used to indicate that a given use refers to a specialization of a function template even when a non-template function ([dcl.fct]) is visible that would otherwise be used. For example:

// template <class T> int f(T);    // #1
int f(int);                     // #2
// int k = f(1);                   // uses #2
int l = f<>(1);                 // uses #1

— end note]

However, we lack the rule to interpret the reason. First, f<> is syntactically ok as per grammar. [basic.lookup] has no special treatment to the name f when have determined that the < is interpreted as the delimiter of a template-argument-list according to [temp.names] p3: discards the name that denotes a non-template function.

The only relevant reason may be [temp.deduct.general] p2:

When an explicit template argument list is specified, if the given template-id is not valid ([temp.names]), type deduction fails.

However, [temp.names] p7 is not friendly to interpret whether a template-id is valid when its component name does not denote a template.

[temp.names] p7 should first admit that a template-id is not valid if its component name does not denote a template, it would be a simple/direct way to interpret the aforementioned case. I think it is also reasonable as the initial condition. [temp.names] p7 may be changed to:

A template-id is valid if

  • the component name of it shall be introduced by a template-declaration
  • [...]

A template-id shall be valid unless it is a simple-template-id that names a function template specialization ([temp.deduct]).

After applying this modification, f<> is not valid if the name f denotes #2, which results in type deduction fails as per [temp.deduct.general] p2. In the current draft, [temp.over] is drafted in terms of the function templates,

If, for a given function template, argument deduction fails or the synthesized function template specialization would be ill-formed, no such function is added to the set of candidate functions for that template.

[temp.over] should also admit that #2 won't be added to the candidate set if it is referenced by an invalid template-id(f<> results in deduction fails). This will interpret why an empty template argument list can be used to enforce to use of the template.

@jensmaurer
Copy link
Member

@opensdh, I thought f<> should remove non-template overloads from the overload set. Do you know where that restriction is?

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 15, 2022

@jensmaurer That is [temp.arg.general] p8

When name lookup for the component name of a template-id finds an overload set, both non-template functions in the overload set and function templates in the overload set for which the template-arguments do not match the template-parameters are ignored.

@jensmaurer
Copy link
Member

Great. So, what is the remaining issue, exactly?

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 15, 2022

No hazy anymore.

@xmh0511 xmh0511 closed this as completed Mar 15, 2022
@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 15, 2022

@jensmaurer The first change may be an improvement

A template-id is valid if

  • the component name of it shall be introduced by a template-declaration.

A template-id shall be valid unless it is a simple-template-id that names a function template specialization ([temp.deduct]).

using type = X<int>; // is not valid template-id

@xmh0511 xmh0511 reopened this Mar 15, 2022
@jensmaurer
Copy link
Member

jensmaurer commented Mar 15, 2022

You can't form a template-id if you don't have a template-name to start with (unless it's a conversion operator template or a literal operator template). A template-name only comes to be from lookup of an identifier that is declared as a template.

@opensdh
Copy link
Contributor

opensdh commented Mar 15, 2022

The non-simple-template-id possibilities can name only function template specializations (but operator overloads, not conversion functions, @jensmaurer), so the latter part of the suggested change does nothing.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 16, 2022

@jensmaurer @opensdh Whether a name is interpreted as a template-name depends on whether the symbol < is interpreted as a delimiter of a template-argument-list, this is implied by [temp.names] p3 and explicitly specified by [temp.names.note] p1

If the name is an identifier, it is then interpreted as a template-name.

There is no rule that specifies that a template-name must be introduced by a template declaration, even though it's indeed a template declaration:

template<class T>
class X{};

which is grammatically equivalent to template-head declaration where the declaration is a class-specifier whose class-name is an identifier that is not a template-name on this grammar chain.

Consider this example:

struct C{
   struct X{};
};
template<class T>
T::X<int> fun(){/*...*/ };

fun<C>();

According to [temp.names] p3 and [temp.res.general] p4, the symbol < is interpreted as a delimiter

A < is interpreted as the delimiter of a template-argument-list if it follows a name that is not a conversion-function-id and

  • [...]
  • that is a terminal name in a using-declarator ([namespace.udecl]), in a declarator-id ([dcl.meaning]), or in a type-only context other than a nested-name-specifier ([temp.res]).

Thus, X<int> is interpreted as a simple-template-id, this is the initial syntax analysis in the first phase. Any further checking will be done when the template is instantiated. So, we should ensure that we have the rule to specify whether X<int> is a valid template-id or not at that point.

@jensmaurer
Copy link
Member

jensmaurer commented Mar 16, 2022

The temp.names p3 rules help to determine at parse time whether some < is an operator or the start of a template-argument-list. The interpretation whether something is a template-name or not is locked in at parse time (with the help of the < disambiguation rules), not determined at instantiation time. The "is valid" checks need to be performed at instantiation time, though, which is too late.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 16, 2022

The interpretation whether something is a template-name or not is locked in at parse time (with the help of the < disambiguation rules)

So, for T::X<int> this stuff, the delimiter is interpreted as a start of a template-argument-list and the terminal name of this type-specifier is X that is an identifier, It can be considered as a template-name at parse time and such a result is locked. This is the issue here, for which we have no special rule to restrict what T::X<int> will be when it's instantiated. Since X<int> is locked to be considered to be a template-id at parse time, it should be consistent at any time, hence, I think the valid definition for a template-id should work on it to check whether it is valid. This is the motivation of the modification.

Even, we can simplify the example to:

struct C{
   struct X{};
};
C::X<int> fun();

[temp.names]/3.4 still interpret the symbol < as the start of a template-argument-list, thus X<int> is a template-id, at this point, which rule explicitly states such case is ill-formed? If we would have such a rule

A template-id is valid if

  • the component name of it shall be introduced by a template-declaration.

The reason will be obvious.

@jensmaurer
Copy link
Member

We say that a class-name is introduced by a class declaration, and lookup essentially returns a class-name when it finds a name declared as a class.

We have something similar for _template-name_in temp.param p3 (a template template parameter introduces a template-name); we seem to be lacking wording that a template declaration introduces its declarator-id as a template-name.

Once we have that, the < would cause a template-name to be required at the point of use, but the lookup result doesn't fit, and it's ill-formed. Similar to X::Y requiring X to be a class-name or namespace-name.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 16, 2022

We say that a class-name is introduced by a class declaration, and lookup essentially returns a class-name when it finds a name declared as a class.

We have something similar for _template-name_in temp.param p3 (a template template parameter introduces a template-name); we seem to be lacking wording that a template declaration introduces its declarator-id as a template-name.

Once we have that, the < would cause a template-name to be required at the point of use, but the lookup result doesn't fit, and it's ill-formed. Similar to X::Y requiring X to be a class-name or namespace-name.

It seems it is exact #5345. We have a violent agreement.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 16, 2022

lookup essentially returns a class-name when it finds a name declared as a class.

It seems that this is not explicitly espoused with a formal rule. lookup only concerns name and declarations, where name is defined as:
[basic.pre] p4

A name is an identifier ([lex.name]), operator-function-id ([over.oper]), literal-operator-id ([over.literal]), or conversion-function-id ([class.conv.fct]).

That is, we still lack the wording that once an identifier(name) is determined to be an entity, the identifier will be promoted to the corresponding component(e.g. class-name, namespace-name, or declarator-id) of that declaration. The grammar together with rules is used to restrict language constructs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants