6

I'm reading about the template keyword qualifier (https://www.ibm.com/support/knowledgecenter/SSPSQF_9.0.0/com.ibm.xlcpp111.aix.doc/language_ref/keyword_template_qualifier.html, and Where and why do I have to put the "template" and "typename" keywords?), but there's still something that confuses me.

Is it possible to have code like this, that compiles successfully, but results in two different operations?

SomeObjectInstance.template some_function();
SomeObjectInstance.some_function();
Nickolai
  • 1,579
  • 2
  • 12
  • 19
  • 3
    I don't know how good ibm's documentation is, but I usually find [cppreference](http://en.cppreference.com/w/cpp) to be excellent; maybe it would be easier to understand [the documentation there](http://en.cppreference.com/w/cpp/language/dependent_name) – Justin Jan 24 '18 at 21:16
  • 2
    @HolyBlackCat: Yes, something like that would probably be an example. Some code later in the program would have to change also, but I think we could come up with an example of two lines that both are part of a valid program and differ only in presence of the `template` keyword. `template class TC;` followed by `TC::template x<1 && 2> y;` and `TC::x<1 && 2>y;` could both be legal depending on the later specializations. – Ben Voigt Jan 24 '18 at 21:24
  • @Justin thanks for the reference. The section "The `template` disambiguator for dependent names" was helpful. Still don't feel like I've fully grokked this stuff, but this is helpful. – Nickolai Jan 25 '18 at 01:44

1 Answers1

2

Yes, you could write something like that, have it be well-formed, and give different results. Basically your own example:

#include <iostream>

struct foo {
    template <int = 0>
    void some_function() { std::cout << "template\n"; }

    void some_function() { std::cout << "non-template\n"; }
};


int main(void) {
    foo f{};

    f.some_function();
    f.template some_function();

    return 0;
}

Will print what you expect. When the compiler sees the first call to some_function, it must check if it can synthesize the template overload. Which it can, since we provided a default argument to the template parameter. Then it does overload resolution with both candidates, and [over.match.best]/1 tells us that

Given these definitions, a viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then

  • F1 is not a function template specialization and F2 is a function template specialization, or, if not that,

We have two equally good implicit conversion sequences (empty), and that bullet explicitly says the non-template version is a better candidate given everything else is the same.

But when the template keyword is used, we defer to [temp.names]/5:

A name prefixed by the keyword template shall be a template-id or the name shall refer to a class template or an alias template.

A template-id has this grammar production in paragraph 1:

simple-template-id:
  template-name < template-argument-list >

template-id:
  simple-template-id
  operator-function-id < template-argument-list >
  literal-operator-id < template-argument-list >

The template-argument-list is optional in all of the above. But an astute reader will notice that the angle brackets aren't specified as optional. It seems that we must name the template member function as some_function<>. But fortunately we are doing a function call. Template argument deduction is happening, and it is for this reason that [temp.arg.explicit]/3 can be applied:

... If all of the template arguments can be deduced, they may all be omitted; in this case, the empty template argument list <> itself may also be omitted. ...

So we indeed may write f.template some_function();, and it must refer to a template, according to [temp.names]/5. That should have the effect of removing the non-template overload from consideration.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • 1
    You may have uncovered a gcc bug. I tried a near-identical example, but used gcc with different results. [Here's your example with gcc](http://coliru.stacked-crooked.com/a/2a25cabc3ea70f72) – Drew Dormann Jan 24 '18 at 21:54
  • With your argument about [temp.arg.explicit]/3, can you explain why the example from the standard that you linked, `a.template f(t);` is an error? – M.M Jan 30 '18 at 20:55
  • @M.M - The note above the example says "The keyword template may not be applied to non-template members of class templates." Which is the case in the standard's example. I didn't find anything similar for member templates of non-template classes. I did look, that's why I deleted and undeleted the answer. – StoryTeller - Unslander Monica Jan 30 '18 at 21:07
  • Really awesome example and explanation, thank you. It sounds like there's nothing inherent about why this needs to be so. It seem like, apart from cases where the template parameter is a numerical value, the compiler should have enough information to deduce which overload to use, but the current standard simply doesn't allow it? This reminds of a while ago when code of the form `vector>` wouldn't compile without a space between >> to disambiguate it from the extraction operator. Later compilers could handle this. Would you say this is similar? – Nickolai Feb 12 '18 at 02:10
  • @Nickolai - I'm not sure I follow you. The standard doesn't allow what exactly? – StoryTeller - Unslander Monica Feb 12 '18 at 05:32
  • Let me put it this way, could you answer my original question without using default values for the template parameter, or using a template parameters that's not a primitive type? I have yet to see such an example, and if that's the case, then why is the `template` keyword required? It would seem that the compiler has enough information to resolve whatever is ambiguous without disambiguating statements like `template`, and it's only the standard that's preventing it from doing so? – Nickolai Feb 12 '18 at 06:04
  • 1
    @Nickolai - Well, the same [tweaked example](http://coliru.stacked-crooked.com/a/cf68bdb399562716). With a type parameter. And it's not *required*. Templates participate in overload resolution regardless. But regular functions with matching parameter types are favored over generated template specializations (again see the example). You can also disambiguate with `some_function<>`. – StoryTeller - Unslander Monica Feb 12 '18 at 06:08