12

Consider this piece of code:

template<typename FirstArg>
void foo()
{
}

template<typename FirstArg, typename... RestOfArgs>
void foo()
{
    foo<RestOfArgs...>();
}

int main()
{
    foo<int, int, int>();
    return 0;
}

It does not compile due to ambiguous call foo<RestOfArgs...>(); when RestOfArgs has only one element ({int}).

But this compiles without error:

template<typename FirstArg>
void foo(FirstArg x)
{
}

template<typename FirstArg, typename... RestOfArgs>
void foo(FirstArg x, RestOfArgs... y)
{
    foo(y...);
}

int main()
{
    foo<int, int, int>(5, 6, 7);
    return 0;
}

Why is there ambiguity in the first case?

Why is there no ambiguity in the second case?

rubix_addict
  • 1,811
  • 13
  • 27
  • My assumption is that it has something to do with the fact that function's signature are its arguments, and that `` and `` are indistinguishable in one case, but in the other somewhy. – yeputons Feb 06 '17 at 08:22
  • Look at [overload_resolution](http://en.cppreference.com/w/cpp/language/overload_resolution), and [Function_template_overloading](http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading) – Jarod42 Feb 06 '17 at 08:45
  • @Jarod42 I looked at it but still don't know the answer to my questions. – rubix_addict Feb 14 '17 at 14:41
  • @rubix_addict: I agree that it is not obvious from the given links. I highlight the point in an answer. – Jarod42 Feb 14 '17 at 19:51

3 Answers3

4

The answer by @ZangMingJie answers the difference in behavior your are observing in your code.

I found it easier to understand the name resolution with the following change:

template<typename FirstArg>
void foo()
{
    printf("1\n");
}

template<typename FirstArg, typename SecondArg, typename... RestOfArgs>
void foo()
{
    printf("2\n");
    foo<SecondArg, RestOfArgs...>();
}

int main()
{
    foo<int, int, int>();
    return 0;
}

When two or more template parameters are used, the second function gets invoked. When one template parameter is used, the first function gets invoked.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • This works and solves the problem of ambiguity but I still don't know why. My original question has not been answered yet. – rubix_addict Feb 14 '17 at 14:40
  • @rubix_addict, I thought the answer by Zang did explain the issue. – R Sahu Feb 14 '17 at 18:34
  • @RSahu: I don't think so, as he explains, SFINAE on content of definition of the function would be used, which is wrong :-/ – Jarod42 Feb 14 '17 at 19:50
3

In Function template overloading

There is lot of rules to tell which template function is more specialized (according to given parameters).

The point which makes

template<typename> void foo();
template<typename, typename...> void foo();

ambiguous for foo<int>(), but not

template<typename T> void foo(T);
template<typename T, typename... Ts> void foo(T, Ts...);

for foo(42) is the following:

In case of a tie, if one function template has a trailing parameter pack and the other does not, the one with the omitted parameter is considered to be more specialized than the one with the empty parameter pack.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 3
    Wouldn't this sentence suggest that there shouldn't be any ambiguity in both cases? Well, the "trailing parameter pack" is kinda... ambiguous (pun intended) - it can apply to both "trailing template parameter pack" and "trailing function parameter pack". – rubix_addict Feb 17 '17 at 16:04
  • I agree that it is not clear enough IMO, but "trailing" won't apply to `template void f(Leading, std::tuple, Trailing)` vs `template void f(A, std::tuple<>, B)`. which is similar to your cases. – Jarod42 Feb 17 '17 at 18:34
  • 1
    but in both of my cases parameter packs are "trailing" – rubix_addict Feb 20 '17 at 11:43
  • I meant that trailing parameters, apply to the parameter of the function, not of the template (which would make some in-coherency with above example). – Jarod42 Feb 20 '17 at 12:24
1

Why is there ambiguity in the first case?

RestOfArgs can be empty.

So foo<int> can be instantiated as:

template<int>
void foo()
{
}

and

template<int,>
void foo()
{
    foo<>();
}

both will compile, so it is ambiguous.

Actually foo<>() won't compile, but it fails in the next instantiation, so it doesn't matter.

Why is there no ambiguity in the second case?

foo<int>(7) can be instantiated as:

template<int>
void foo(int 7)
{
}

and

template<int>
void foo(int 7)
{
    foo();
}

but the second one is an error, because there are no foo taking no argument, so the only candidate is the first one, so there won't be ambiguous

Zang MingJie
  • 5,164
  • 1
  • 14
  • 27
  • Would you mind explaining [this example](http://ideone.com/ZQz8Le) as well? I've removed `foo` calls and it still compiles. – yeputons Feb 06 '17 at 08:33
  • @yeputons sure, without recursive call, only VAARG template is used. – Zang MingJie Feb 06 '17 at 08:36
  • @yeputons, don't use SO as a consulting service. This is a good answer to your question. If you have other related questions, please post another question. – R Sahu Feb 06 '17 at 08:37
  • @ZangMingJie so in the second case the second foo overload is removed from the overload set due to SFINAE, right? – rubix_addict Feb 06 '17 at 09:08
  • @rubix_addict I thought that as well (see my comments to two answers here), but looks like it's not the case (see my example). If you omit call to `foo` in the second overload, it still compiles when arguments are specified (yet unused), but does not when they are not. – yeputons Feb 06 '17 at 09:41
  • @RSahu I should have been more specific. I believe that this post _does not fully answer the question_ asked by OP and can be significantly improved, so I'm asking for that. It shows one possible reason for the observed behavior, but even if that reason if removed, the behavior is still observed, see [my example](http://ideone.com/ZQz8Le). – yeputons Feb 06 '17 at 09:45
  • You seems to say that overload is rejected according to definition/content of the function, which is wrong. Only the declarations are used to select overloads. – Jarod42 Jun 28 '22 at 16:09