2015-02-03 2 views
5

У меня есть два оператора шаблона в классе:Каковы правила выбора между методом вариационного шаблона и обычным методом шаблонов?

template<class T> 
    size_t operator()(const T& t) const { 
     static_assert(boost::is_pod<T>(), "Not a POD type"); 
     return sizeof t; 
    } 

    template<typename... T> 
    size_t operator()(const boost::variant<T...>& t) const 
    { 
     return boost::apply_visitor(boost::bind(*this, _1), t); 
    } 

проходит boost::variant<some, pod, types, here> в качестве аргумента этих операторов. GCC 4.8 и llvm 6.0 скомпилируют код, выбрав boost::variant параметризованный оператор. gcc 4.7 выбирает const T& t параметризованный оператор и, следовательно, не может скомпилироваться из-за статического утверждения.

Итак, у меня есть вопрос, каковы правила выбора между этими двумя? Я думаю, что gcc 4.7 должен иметь ошибку, но у меня нет никаких доказательств.

ответ

0

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

Действительно, это означает, что GCC 4.7 имеет ошибку.

См §14.8.3: Разрешение перегрузки

описывает, что все экземпляры шаблона будут участвовать в наборе кандидатов как любой не-шаблон объявлен перегрузка:

Шаблон функции может быть перегружен либо (не шаблонные) функции своего имени или с помощью (других) шаблонов функций с тем же именем. Когда вызов этого имени записывается (явно или неявно с использованием операторной нотации), вывод аргумента шаблона (14.8.2) и проверка любого явного шаблона аргументы (14.3) выполняются для каждого шаблона функции для поиска шаблона значения аргументов (если они есть), которые могут быть использованы с этим шаблоном функции до , создают экземпляр специализированной функции, которая может быть вызвана с помощью аргументов вызова . Для каждого шаблона функции, если проверка аргумента и проверка успешны, аргументы шаблона (выведенные и/или явные) используются для синтеза объявления специализации шаблона одной функции , которая добавляется к кандидатам, установленным как функция используется при перегрузке разрешение. Если для данного шаблона функции сбой вывода аргумента отсутствует, то такая функция добавляется к набору функций-кандидатов для этого шаблона. полный набор функций-кандидатов включает в себя все синтезированные объявления и все перегруженные функции без шаблона с тем же именем. Синтезированные декларации рассматриваются как любые другие функции в остатке с разрешением перегрузки, за исключением случаев, указанных в 13.3.3.

В случае Вашего вопроса, то в конечном итоге перегруженных неразличимости (кредит: @Piotr S). В таких случаях «частичное упорядочение» применяется (§14.5.6.2): ​​

F1 и F2 шаблон функции специализации, а шаблон функции для F1 является более специализированным, чем шаблон для F2

Обратите внимание, что все может стать довольно сложным, когда, например,версия «открытого шаблона» взяла T& вместо T const& (предпочтительны ссылки без const, при прочих равных условиях).

Когда у вас было несколько перегрузок, которые в конечном итоге имеют одинаковый «ранг» для разрешения перегрузки, вызов плохо сформированный, и компилятор будет диагностировать неоднозначный вызов функции.

+2

Это не * выбор наилучшего соответствия *, как в ранжировании конверсий. Это частичный порядок, который делает последнюю перегрузку предпочтительной для аргумента boost :: variant. –

+0

@PiotrS. Я думаю, что ваш ответ противоречит цитируемому разделу. Я также не уверен, что PO - проблема с перегрузками, объявленными внутри одного класса. (Этот трюк часто используется для рекурсивных/вариационных рассылок, которые не будут работать в области пространства имен) – sehe

+2

Что такое встречный раздел? после создания экземпляров обоих шаблонов функций они неотличимы друг от друга, поэтому PO считается: «* F1 и F2 являются специализированными шаблонами функций, а шаблон функции для F1 более специализирован, чем шаблон для F2 в соответствии с правилами частичного упорядочения, описанными в 14.5 .6.2. "* –

3

Ключевой раздел в [temp.deduct.partial]:

Два набора типов используются для определения частичного упорядочения. Для каждого из задействованных шаблонов есть исходный тип функции и преобразованный тип функции. [Примечание: создание преобразованного типа описано в 14.5.6.2. -end note] Процесс дедукции использует преобразованный тип в качестве аргумента шаблона и исходного типа другого шаблона в качестве шаблона параметра. Этот процесс выполняется дважды для каждого типа, участвующего в сравнении частичного заказа: один раз с использованием преобразованного шаблона-1 в качестве шаблона аргумента и шаблона-2 в качестве шаблона параметра и снова с использованием преобразованного шаблона-2 в качестве шаблона аргумента и template-1 в качестве шаблона параметра.

Это действительно плотно, даже для стандарта C++, но в основном это означает. Возьмите нашу две перегрузки:

template <class T> // #1 
size_t operator()(const T& t) const 

template <typename... T> // #2 
size_t operator()(const boost::variant<T...>& t) 

И мы будем в основном присвоить некоторый уникальный тип (ы) для каждого из них и попытаться увидеть, если другие применяются. Итак, возьмем какой-то тип A за #1 и B,C,D за #2. Делает operator()(const A&) Работает на #2? Нет. operator()(const boost::variant<B,C,D>&) Работает на #1? Да. Таким образом, правила частичного заказа указывают, что #2 более специализирован, чем #1.

И так, из [temp.func.order]:

Процесс дедукции определяет, является ли один из шаблонов является более специализированным, чем другой. Если , тем более специализированным шаблоном является тот, который выбирается процессом частичного заказа.

А из [over.match.best]:

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

Таким образом, #2 следует выбирать в любом случае, когда он применяется. Если GCC выбирает #1, это несоответствующее поведение и является ошибкой.

+1

очень красиво объяснено! спасибо – rightaway717

Смежные вопросы