static_cast не работает с приоритетом, как ожидалось

#include <iostream>
#include <cstdint>

template<int T> void foo()
{
std::cout << "a" << std::endl;
}

template<uint8_t T> void foo()
{
std::cout << "b" << std::endl;
}

int main()
{
foo<static_cast<uint8_t>(42)> ();
foo<static_cast<int>(42)>();
return(0);
}

Любая идея, почему это не работает, как ожидалось?

Мой gcc 4.8.1 жалуется на неоднозначный вызов, но static_cast не должен «исправлять» правило приоритета в случаях, подобных этому, когда у вас есть 2 типа с одинаковым приоритетом?

9

Решение

Можно подумать, что компилятор при разрешении перегруженных шаблонов функций пытается выяснить, какой из шаблонов лучше соответствует заданным аргументам. Исходя из этого предположения, шаблон с uint8_t должен соответствовать вызов функции с uint8_t аргумент лучше чем шаблон для int,

Но это не то, как работает разрешение перегрузки шаблона. Разрешение перегрузки шаблона (§14.5.6.2) отличается от разрешения перегрузки обычной функции (§13.3). Сначала он устанавливает шаблоны-кандидаты, а затем, вместо того, чтобы пытаться проверить, насколько хорошо каждый из них соответствует заданным аргументам, он просто устанавливает, какой из двух (или более) шаблонов-кандидатов является наиболее специализированным.

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

Так что он проверяет, uint8_t более специализированный, чем int или наоборот (в общем случае — не по отношению к заданным аргументам вызова функции под рукой). Он делает это, в основном, проверяя, есть ли uint8_t аргумент (в теории) может быть использован для заполнения int параметр без нестандартных преобразований и наоборот. Это так (в обоих направлениях), поэтому ни один шаблон не является более специализированным, чем другой. Следовательно, двусмысленность не может быть решена.


Соответствующие разделы стандарта следующие.

Во-первых, §13.3.3 устанавливает, что когда два шаблона функций (в отличие от двух обычных функций или одной функции и одного шаблона) конкурируют за вызов функции, механизм перегрузки шаблона используется для выбора наилучшего:

[…] жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция
F2, если для всех аргументов i ICSi (F1) не хуже последовательности преобразования, чем ICSi (F2), а затем

[…] — F1 и F2 являются специализациями шаблонов функций, а шаблон функций для F1 более специализирован, чем шаблон для F2, согласно правилам частичного упорядочения, описанным в 14.5.6.2.

Тогда §14.5.6.2 очень длинный, но наиболее важными частями являются:

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

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

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

Идея здесь такова: возьмите uint8_t шаблон и преобразование это путем замены uint8_t параметр с фактическим синтезированным значением (я полагаю, что это значение может быть взято из фактического вызова функции, но стандарт этого не говорит). Затем используйте процесс вывода типа, чтобы проверить, будет ли преобразованный шаблон, взятый как вызов функции, «соответствовать» Другой шаблон (то есть int шаблон), т.е. если int Параметр другого шаблона мог быть выведен без нестандартных преобразований. Ответ — да, это возможно.

Тогда идите другим путем, возьмите int шаблон, синтезировать значение и попробуйте, если это «соответствует» uint8_t шаблон, т.е. если uint8_t параметр может быть выведен без нестандартных преобразований. Ответ снова да.

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

Заметка. Вся процедура на самом деле более сложная, и ее описание в Стандарте очень длинное, в основном по следующим причинам:

  • Сам процесс вывода типа сложен. Он допускает определенные неявные преобразования (в основном, стандартные преобразования, в том числе связанные с cv-квалификаторами);
  • Он имеет ряд специальных правил арбитража для случая, когда один параметр-кандидат является константной ссылкой, а другой — неконстантной ссылкой, и некоторых подобных случаях;
  • Каждый шаблон-кандидат может приводить к нескольким преобразованным шаблонам, особенно, когда нужно вывести более одного параметра шаблона;
  • Наличие стандартных аргументов, а также пакетов параметров шаблона еще больше усложняет ситуацию.
5

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]