Почему шаблон 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) {}

Попытка пройти шаги стандарта C ++ 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?


person Vyacheslav Grigoryev    schedule 23.01.2018    source источник
comment
Извините, но этот вопрос написан запутанно. Я не понимаю, чему соответствуют case 1 и case 2, ни как вы его называете в каждом примере, ни почему U1 не соответствует std::size_t. В более общем плане я бы добавил, что преобразование массива в указатель является удивительно агрессивным, и в прошлом было сложно выяснить, действительно ли это преобразование влияет на порядок. Однако на SO уже есть несколько таких вопросов.   -  person Nir Friedman    schedule 23.01.2018
comment
Я не уверен, что речь идет о частичном порядке, но может быть, что одна функция лучше соответствует первому параметру, а другая - второму параметру.   -  person Bo Persson    schedule 23.01.2018
comment
1. при частичном упорядочивании вызовов функций не используется тип функции; он использует типы параметров индивидуально. 2. Известно, что в спецификации в этой области есть проблемы. Для этого неясно, проверяются ли подлинность оригинала и выведенного A на идентичность во время частичного упорядочивания и при каких обстоятельствах. Если они так проверены, тогда P=char * и A=char [N1] не будут вычитать. (То есть, если мы проигнорируем сомнительное добавление CWG 1391 к [temp.deduct.partial] p4, которое вызывает собственные проблемы.)   -  person T.C.    schedule 24.01.2018
comment
@ T.C. Итак, если бы CWG 1391 была принята во внимание, вычет A / P не продолжился бы, и шаблон II стал бы более специализированным?   -  person Oliv    schedule 25.01.2018
comment
Первый аргумент шаблона параметра вообще не тип, и он совместим с char [], что вы имеете в виду, когда это вообще не тип? Насколько я понимаю, первая дедукция не удалась (P = char *, но A = char [N1]). Но P не используется для определения порядка, поэтому сбой игнорируется. Итак, в целом II более специализирован, чем I.   -  person Johannes Schaub - litb    schedule 26.01.2018
comment
Интересно, чего вы ожидаете от ответа. Если компиляторы не соответствуют стандарту C ++, тогда ответ прост. Вы можете спросить по той же причине, почему Visual Studio 2006 несовместима с C ++ 03. Это, кстати, не единственный случай, когда компиляторы не соответствуют требованиям. Некоторые из моих 100 отчетов об ошибках юристов в Clang и GCC все еще открыты спустя годы после моих отчетов.   -  person Johannes Schaub - litb    schedule 26.01.2018
comment
@ T.C. почему изменение проблематично?   -  person Johannes Schaub - litb    schedule 26.01.2018
comment
@ JohannesSchaub-litb Он делает неупорядоченными некоторые вещи, которые, вероятно, следует заказать (template<class> void f(int); template<class T> void f(T); f<int>(0);), и заказывает некоторые вещи, которые, вероятно, должны быть неупорядоченными (template<class T> void f(T, short); template<class T> void f(T*, char); f((char*)0, 0);)   -  person T.C.    schedule 26.01.2018


Ответы (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));

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

person apostol    schedule 10.04.2018
comment
Первоначальный вопрос касался неоднозначности между двумя специализациями шаблонов, когда второй аргумент не участвует в разрешении неоднозначности. Для этого второй аргумент должен быть точно типа std :: size_t. Ваш образец не отвечает на вопрос и не доказывает, что объявления шаблона не являются двусмысленными, потому что первый или второй аргумент становится более специализированным. Если вы попытаетесь вызвать свой шаблон как func (buf, static_cast ‹std :: size_t› (2)), вы получите исходную проблему. - person Vyacheslav Grigoryev; 11.04.2018