15
votes

Why are the next two template declarations ambiguous (so neither is more specialized than the other)? I know this question has been raised many times on Stack Overflow, but usually, people answer how to resolve ambiguity, not why it's happened.

I. template <class T> void func(char* buf, T size) {}

II. template <std::size_t N> void func(char (&buf)[N], std::size_t size) {}

Trying to pass steps of the C++14 standard to resolve partial function template ordering (14.5.6.2):

To produce the transformed template, for each type, non-type, or template template parameter (including template parameter packs (14.5.3) thereof) synthesize a unique type, value, or class template respectively and substitute it for each occurrence of that parameter in the function type of the template.

Transformed function I template's function type is: void func(char*, U1), where U1 is some unique synthetic type.

Transformed function II template's function type is: void func(char (&buf)[N1], std::size_t), where N1 is some unique synthetic value.

Using the transformed function template’s function type, perform type deduction against the other template as described in 14.8.2.4.

So let's try to perform type deduction on one side (using the first template as an argument and the second one as a parameter template) and on the opposite side.

Case 1.

Parameter template: template <std::size_t N> void func(char (&buf)[N], std::size_t size). Transformed argument template: void func(char*, U1).

Trying to deduce template parameters. "char (&buf)[N]" can't be deduced from "char*" type. U1 doesn't match std::size_t type either. Failed.

Case 2.

Parameter template: template <class T> void func(char* buf, T size). Transformed argument template: void func(char (&buf)[N1], std::size_t).

Trying to deduce template parameters. The first argument of parameter template is not type at all and it's compatible with a char[]. T should be deduced to std::size_t.

So template II should be more specialized and should be selected in the following code:

char buf[16];
func(buf, static_cast<std::size_t>(16));

Why is this not true for GCC 5.3 and for Clang 4.0?

1
Sorry to say but this question is kind of confusingly written. I don't understand what case 1 and case 2 correspond to, nor how you are calling it in each example, nor why U1 wouldn't match std::size_t. More generally, I would add that array to pointer conversions are amazingly aggressive, and it's been tricky in the past figuring out whether that conversion actually affects ordering. There are multiple such questions already on SO, though.Nir Friedman
I'm not sure this is about partial ordering, but could be that one function is a better match on the first parameter and the other on the second parameter.Bo Persson
1. partial ordering for function calls don't use the function type; it uses the parameter types individually. 2. The specification in this area is known to have issues. For this one, it is not clear whether, or under what circumstances, the original and deduced A are checked for identity during partial ordering. If they are so checked, then P=char * and A=char [N1] will fail deduction. (That is, if we ignore CWG 1391's questionable addition to [temp.deduct.partial]p4, which causes its own problems.)T.C.
@T.C. So if CWG 1391 were taken into account, this A/P deduction would not proceed and template II would be considered more specialized?Oliv
I wonder what you expect the answer to be. If compilers fail to be compliant to the C++ Standard, then the anwer is simply that. You can ask the same reason with "why Visual Studio 2006 is not compliant to C++03". This is BTW not the only case where the compilers are not compliant. Some of my 100 lawyer bug reports to Clang and GCC are still open years after my reports. bugs.llvm.org/…Johannes Schaub - litb

1 Answers

1
votes

The template declarations are not ambiguous; the following code compiles and runs OK:

#include <iostream>
#include <string>

using namespace std;

template<class T>
void func(char* buf, T size) {cout<<"void func(char*,T)\n";}
template<size_t N>
void func(char (&buf)[N], std::size_t size) {
  cout<<"void func(char (&)[],size_t)\n";}

int main() {
  char buf[3];
  func(buf, 2);
  func<3>(buf, 2);
  func(reinterpret_cast<char (&)[3]>(buf), 2);
  //next is ambiguous
  //func(reinterpret_cast<char (&)[3]>(buf), size_t(2));
  func<3>(reinterpret_cast<char (&)[3]>(buf), size_t(2));
  return 0;
}

However, the commented-out call is ambiguous. To disambiguate it use:

func<3>(reinterpret_cast<char (&)[3]>(buf), size_t(2));

This works OK and calls the correct function.