Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a C++ template accepting an array not more specialized than one accepting a pointer according to GCC 5.3 and Clang 4.0?

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?

like image 850
Vyacheslav Grigoryev Avatar asked Jan 23 '18 16:01

Vyacheslav Grigoryev


1 Answers

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.

like image 115
apostol Avatar answered Oct 20 '22 06:10

apostol