Решение, какую структуру специализации шаблона использовать каждый раз - PullRequest
0 голосов
/ 04 июля 2019

У меня есть следующие объявления в C++:

template<class T1, class T2> struct canAssign {
    enum { result = false };
};
template<class T1> struct canAssign<T1,T1> {
    enum { result = true };
};

И у меня есть следующие звонки (A extends B):

canAssign<A,A>::result;
canAssign<B,A>::result;
canAssign<B*,A*>::result;

Я знаю, что он использует функцию template specialization, но я не могу понять, как он решает, какой из них выбирать каждый раз?

РЕДАКТИРОВАТЬ : Я знаю, каким должен быть вывод:

true
false
false

У меня вопрос, как компилятор выбирает правильный? какой алгоритм / метод / подход мне нужно подумать, чтобы понять, какой из них будет называться

Ответы [ 4 ]

0 голосов
/ 05 июля 2019

Это тип сопоставления с образцом.

Это будет просто история. Он не совсем соответствует стандарту, но я считаю, что он генерирует хорошую интуицию.

template<class T1, class T2> struct canAssign {
  enum { result = false };
};

это первичная специализация. Он определяет сигнатуру шаблона - два класса, как определено <class T1, class T2>. Это также тот, который создается, если больше ничего не соответствует.

template<class T1> struct canAssign<T1,T1> {
  enum { result = true };
};

это вторичная специализация. Здесь <class T1> служит другой цели; он вводит переменные для сопоставления с образцом, а не аргументы.

Аргументы находятся в <T1,T1> части. Компилятор сопоставляет <T1,T1> с переданными аргументами.

Любой аргумент в «выведенном контексте» сопоставляется независимо. Так что, если вы передадите <A,B>, оно будет соответствовать T1=A, тогда T1=B. Это противоречиво; специализация не совпадает. Если вы передадите <A,A>, то совпадет с T1=A и T1=A. Это соответствует.

Вещи становятся более причудливыми, когда вы делаете <T1*,T1*>. Затем, если вы передадите <A,A>, он попытается T1*=A и потерпит неудачу; но если передать <A*,A*>, он будет начинаться с T1*=A*, затем убрать * и получить T1=A, а затем сделать это снова для второго аргумента.

Не выведенные контексты отличаются. Когда у вас есть не выведенный контекст, такой как std::enable_if_t<some_bool, T1>, сопоставление с этим аргументом не выполняется. Вместо этого другие аргументы соответствуют шаблону, тогда переменные шаблона специализации заменяются. Если результирующий тип точно соответствует аргументу, он проходит; в противном случае это не удастся.

Последний бит - это порядок. Если две вторичные специализации проходят, то выигрывает тот, кто строго «более специализирован». Если ни один не является строго более специализированным, ни один не выигрывает. Правила для более специализированных сложны; Интуитивно понятная идея состоит в том, что если одна специализация может всегда соответствовать случаям другой, но не наоборот, то, что строго соответствует, является более специализированной.

0 голосов
/ 04 июля 2019

Мой вопрос: как правильно выбрать компилятор?какой алгоритм / метод / подход мне нужно подумать, чтобы понять, какой из них будет называться

Алгоритм описан в [temp.class.spec.match] раздел (и связанные с ним) конкретного стандарта, на который вы нацелены.

0 голосов
/ 05 июля 2019

Мой вопрос: как правильно выбрать компилятор?какой алгоритм / метод / подход мне нужно продумать, чтобы понять, какой из них будет называться

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

Версия является более специализированной, чем другая, когда

1) все совпадения параметров шаблона, для более специализированных - также совпадения для менее специализированных

2) и есть совпадения для менее специализированных, которые не совпадают с более специализированными

В вашем случае у вас есть две версии canAssign: основная и специализация.

Специализация, очевидно, более специализирована, чем основная версия, поэтому в случае совпадения версий ботов

canAssign<A,A>::result;

, поэтому компилятор выбирает специализацию.

В остальных случаях

canAssign<B,A>::result;
canAssign<B*,A*>::result;

соответствует только основная версия, поэтому компилятор выбирает основную версию.

Еще один пример для лучшего объясненияe точка: предположим, у вас есть шаблон foo с двумя специализациями

template <typename, typename, typename>
struct foo 
 { };

template <typename A, typename B>
struct foo<A, A, B>
 { };

template <typename A>
struct foo<A, A, A>
 { };

Вторая специализация является более специализированной, чем первая.

Фактически

1) все наборы параметров шаблона (обязательно все равные), соответствующие второму, обязательно соответствуют также первому (первый и второй равны)

2) существует хотя бы набор параметров шаблона (int, int, long, например), который соответствует первому, но не второму.

Итак:

1) вторая специализация более специализированная, чем первая

2) первая специализация более специализированная, чем основная версия

То есть

  • foo<int, int, int> соответствует всем версиям, поэтому компилятор выбирает вторую специализацию

  • foo<int, int, long> соответствует основной версии и первой специализации, поэтому компилятор выбирает первую специализацию

  • foo<int, long, int> соответствует только основной версии, поэтому компилятор выбирает основную версию.

0 голосов
/ 04 июля 2019

Возможное наследование здесь не считается.

Итак, у вас есть:

canAssign<A, A>::result;   // true  T1=A, T2=A as T1=T2, use specialization
canAssign<B, A>::result;   // false T1=B, T2=A
canAssign<B*, A*>::result; // false T1=B*, T2=A*
...