Почему шаблон C++ принимает массив не более специализированный, чем приемник указателя в соответствии с GCC 5.3 и Clang 4.0?

Почему следующие два объявления шаблона неоднозначны (так что ни один из них не является более специализированным, чем другой)? Я знаю, что этот вопрос неоднократно поднимался на Stack Overflow, но обычно люди отвечают, как разрешать двусмысленность, а не почему это произошло.

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) {}

Попытка выполнить шаги стандарта С++ 14 для частичного упорядочения шаблонов функций (14.5.6.2):

Для создания преобразованного шаблона для каждого типа параметр непигового или шаблонного шаблона (включая пакеты параметров шаблона (14.5.3)) синтезирует уникальный шаблон типа, значения или класса соответственно и подставляет его для каждого вхождения этого параметра в типе функции шаблона.

Тип функции трансформации функции I: void func(char*, U1), где U1 - уникальный синтетический тип.

Тип функции шаблона преобразованной функции II: void func(char (&buf)[N1], std::size_t), где N1 - некоторая уникальная синтетическая величина.

Используя тип функции шаблонов преобразованных функций, выполните вывод типа против другого шаблона, как описано в 14.8.2.4.

Поэтому попробуйте выполнить вывод типа с одной стороны (используя первый шаблон в качестве аргумента, а второй - как шаблон параметра) и на противоположной стороне.

Случай 1.

Шаблон параметров: template <std::size_t N> void func(char (&buf)[N], std::size_t size). Преобразованный шаблон аргумента: void func(char*, U1).

Попытка вывести параметры шаблона. " char (&buf)[N] " не может быть выведен из типа " char* ". U1 также не соответствует типу std::size_t. Не удалось.

Случай 2.

Шаблон параметров: template <class T> void func(char* buf, T size). Преобразованный шаблон аргумента: void func(char (&buf)[N1], std::size_t).

Попытка вывести параметры шаблона. Первый аргумент шаблона параметра не является типом, и он совместим с char[]. T следует выводить на std::size_t.

Поэтому шаблон II должен быть более специализированным и должен быть выбран в следующем коде:

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

Почему это не относится к GCC 5.3 и для Clang 4.0?

Ответ 1

Объявления шаблонов не являются двусмысленными; следующий код компилируется и запускается нормально:

#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;
}

Тем не менее, закомментированный звонок неоднозначен. Чтобы устранить эту проблему, используйте:

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

Это работает нормально и вызывает правильную функцию.