Почему эти шаблоны неоднозначны? - PullRequest
6 голосов
/ 14 декабря 2010

Книга Шаблоны C ++: Полное руководство на стр. 275 приведен пример, что у меня возникают проблемы с оборачиванием головы.

Цитирую выдержки из книги ...

template <typename T>
class Promotion<T,T> {
  public:
    typdef T ResultT;
};

template<typename T1, typename T2>
class Promotion<Array<T1>, Array<T2> > {
  public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};

template<typename T>
class Promotion<Array<T>, Array<T> > {
  public:
    typedef Array<typename Promotion<T,T>::ResultT> ResultT;
};

К сожалению, частичная специализация Promotion<Array<T1>, Array<T2> > не является ни более, ни менее специализированной, чем частичная специализация Promotion<T,T>.Чтобы избежать неоднозначности выбора шаблона, была добавлена ​​последняя частичная специализация.Он более специализирован, чем любая из двух предыдущих частичных специализаций.

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

Ответы [ 5 ]

15 голосов
/ 14 декабря 2010

Возможно, ваша путаница проистекает из того, как работает отношение "более специализировано, чем".Это частичный порядок , а не общий порядок - это означает, что с учетом двух специализаций шаблонов не всегда бывает так, что один является более специализированным, чем другой.

Комментарий Анона прав:Предположим, что третьей специализации не существует, и позже в вашем коде у вас было:

Promotion<Array<double>, Array<double> > foo;

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

Учитывая это объявление foo, какая из 1-ых 2 специализаций будет выбрана?

  • Применяется ли специализация 1 с T = Array<double>.
  • Применяется специализация 2 с T1 = double, T2 = double.

Обе специализации применимы, поэтому нам нужно определить, какая из них "более специализированная", чем другая, и выбрать этуодин.Как?Мы скажем, что X более специализирован, чем Y, если он , по крайней мере, столь же специализирован , как Y, но Y не настолько специализирован, как X.Хотя кажется, что это просто танцует вокруг проблемы, есть умное правило, которое мы можем использовать, чтобы ответить на этот новый вопрос:

X по крайней мере так же специализирован, как Y, если,независимо от того, какие типы мы присваиваем параметрам шаблона X, результирующий тип всегда может соответствовать Y.

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

Может ли специализация 1 всегда совпадатьпо специализации 2?Процесс немного похож на алгебру.Мы требуем, чтобы для любого типа T мы могли найти типы T1 и T2 такие, что:

Promotion<Array<T1>, Array<T2> > = Promotion<T, T>

Это означает:

Array<T1> = T
Array<T2> = T

Так что ответ - нет.Если посмотреть только на первый подразумеваемый результат, заданный для любого типа T, в общем случае невозможно найти тип T1 такой, что Array<T1> такого же типа, как T.(Это будет работать, если T окажется Array<long>, но не будет, если T будет int или char* или большинством других типов.)

А как насчет обратного?Может ли специализация 2 всегда соответствовать специализации 1?Мы требуем, чтобы для любых типов T1 и T2 мы могли найти тип T такой, что:

Promotion<T, T> = Promotion<Array<T1>, Array<T2> >

Подразумевается:

T = Array<T1>
T = Array<T2>

Таким образом, ответ снова нет.Для любого типа T1 всегда можно найти тип T такой, что T того же типа, что и Array<T1> - просто буквально установить T = Array<T1>.Но в целом другой тип T2 не обязательно должен совпадать с T1, и если это не так (например, если T1 = bool, но T2 = float), то будет невозможно найти тип T, которыйсовпадает с Array<T1> и Array<T2>.Таким образом, в общем случае невозможно найти такой тип T.

. В этом случае не только ни одна специализация не является более специализированной, чем другая, ни даже не столь специализирована, как Другой.В результате, если возникает необходимость в создании экземпляра этого шаблонного класса, и обе специализации совпадают - как это происходит в примере, приведенном Аноном, - невозможно выбрать «лучший».

3 голосов
/ 14 декабря 2010

j_random_hacker имеет ответил на вопрос, но в качестве конкретного примера, просто используя правила стандарта напрямую:

template< class T >
class Array {};

template< class T1, class T2 >
class Promotion {};

template <typename T>                   // a
class Promotion<T,T> {
public:
    typedef T ResultT;
};

template<typename T1, typename T2>      // b
class Promotion<Array<T1>, Array<T2> > {
public:
    typedef Array<typename Promotion<T1,T2>::ResultT> ResultT;
};

// template<typename T>
// class Promotion<Array<T>, Array<T> > {
//   public:
//     typedef Array<typename Promotion<T,T>::ResultT> ResultT;
// };


//---------------------------- §14.5.4.2/1:

template< class T >
void a_( Promotion< T, T > );                      // a

template< class T1, class T2 >
void b_( Promotion< Array< T1 >, Array< T2 > > );  // b


//---------------------------- §14.5.5.2/3:

class aT {};
class bT1 {};
class bT2 {};

void a( Promotion< aT, aT > );                          // a
void b( Promotion< Array< bT1 >, Array< bT2 > > );      // b

void test()
{
    // Check if the concrete 'a' arguments fit also 'b':
    b_( Promotion< aT, aT >() );
    // Fails, so a is not at least as specialized as b

    // Check if the concrete 'b' arguments fit also 'a':
    a_( Promotion< Array< bT1 >, Array< bT2 > >() );
    // Fails, so b is not at least as specialized as a
}    

Отказ от ответственности: мне пришлось заново учитьЯ снова это делаю.

Я публикую это, потому что кто-то еще нашел мой пример поучительным, поэтому может помочь и читателям здесь.

Приветствия & hth.,

3 голосов
/ 14 декабря 2010

Это потому, что при выводе параметров один шаблон лучше для первого параметра, а другой - для второго.

Давайте рассмотрим Promotion< Array<S>, Array<S> >.

Оба кандидата могут совпадать,По первому параметру Promotion< T, T > соответствует выводу T = Array<S>.Promotion< T1, T2 > соответствует выводу T1 = S.Второе совпадение лучше, поскольку Array<T1> более конкретно, чем T.

По второму параметру Promotion< T = Array<S>, T = Array<S> > - точное совпадение.Promotion< T1 = S, T2 > соответствует выводу T2 = S.Поскольку Array<S> лучше, чем Array<T2>, первый более конкретен.

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

Третья специализация соответствует критериям (первый параметр равен Array<T1>, второй - идеален), поэтому она разрешает неоднозначность.

Если вы ДЕЙСТВИТЕЛЬНО хотите, чтобы ваша голова кружилась, попробуйте прочитать раздел [temp.deduct.partial] в стандарте, где все эти правила изложены в хорошем юристе.Это 14.8.2.4 в проекте n3225, нумерация может отличаться в других версиях.

2 голосов
/ 14 декабря 2010

Посмотрите, что специализации делают на концептуальном уровне.

Первый говорит: «Это то, что мы должны делать конкретно, если T1 и T2 одинаковы».

Второйговорит: «Это именно то, что мы должны сделать конкретно, если T1 и T2 оба являются шаблонами Array».

ОК, так что ... скажем, T1 и T2 оба являются одним и тем же шаблоном массива .Какой из них мы должны использовать?Нет хорошего способа утверждать, что один из них более конкретно приспособлен к этой ситуации, чем другой.

Мы решаем эту проблему, добавляя третий шаблон специализации специально для этой ситуации: «Это то, что мы должны делать конкретноесли T1 и T2 оба являются одним и тем же шаблоном Array ".

2 голосов
/ 14 декабря 2010

Неоднозначность - это когда один и тот же класс может быть создан из разных специализаций, и компилятор не может предпочесть одну специализацию другой, потому что ни одна из них не является более специализированной.Добавление более специализированной версии заставляет компилятор выбирать эту версию.Это устраняет неоднозначность, поскольку правила C ++ требуют, чтобы наиболее специализированный шаблон выбирался из менее специализированных шаблонов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...