Заставить C ++ вызывать правильный шаблонный метод в безобразной форме - PullRequest
7 голосов
/ 08 сентября 2010

Я готовлю векторную библиотеку и наткнулся на загадку. Я хочу разрешить рекурсивные векторы (т.е. vec<H,vec<W,T> >), поэтому я бы хотел, чтобы мои "min" и другие функции также были рекурсивными. Вот что у меня есть:

template<typename T>
inline T min(const T& k1, const T& k2) {
 return k1 < k2 ? k1 : k2;
}
template<int N, typename T, typename VT1, typename VT2>
inline vec<N,T> min(const container<N,T,VT1>& v1, const container<N,T,VT2>& v2) {
 vec<N,T> new_vec;
 for (int i = 0; i < N; i++) new_vec[i] = min(v1[i], v2[i]);
 return new_vec;
}

...

template<int N, typename T>
class vec : public container<N,T,vec_array<N,T> > {

...

// This calls the first (wrong) method and says you can't call ? on a vec
vec<2,float> v1,v2;
min(v1,v2);
// This says the call is ambiguous
container<2,float,vec_array<2,float> > c1,c2;
min(c1,c2);
// This one actually works
vec<2,float> v3; container<N,T,some_other_type> v4;
min(v3,v4);
// This works too
min<2,float,vec_array<2,float>,vec_array<2,float> >(v1, v2);

Последний звонок ужасен! Как я могу вызвать правильный метод только с min(v1,v2)? Лучшее, что я могу придумать, это избавиться от класса "vec" (поэтому v1 и v2 должны быть определены как контейнер <2, float, vec_array <2, float>>) и добавить еще один метод template<N,T,VT> min что вызывает min<N,T,VT,VT>(v1,v2).

Спасибо!

Ответы [ 2 ]

1 голос
/ 08 сентября 2010

У вас будет разрешение перегрузки, которое предпочтительнее первого min для первого случая. Он принимает оба аргумента с точным соответствием, в то время как второй min нуждается в преобразовании в базовое преобразование для принятия аргументов.

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

Тем не менее, в вашем собственном решении вам нужно поставить typename перед типом возврата, чтобы сделать решение стандартным C ++. Я думаю, что проблема, которая заставляет вас определять второй шаблон, состоит в том, что для того, чтобы сделать шаблон более специализированным, первый min min должен принять все аргументы, которые принимает второй шаблон, что выясняется путем простой попытки сопоставить аргументы второго шаблона с первым

container<N, T, VT1> -> T // func param 1
container<N, T, VT2> -> T // func param 2

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

container<N, T, VT> -> T // func param 1
container<N, T, VT> -> T // func param 2

Это заставит первый шаблон выводить все типы параметров из второго шаблона, но не наоборот: container<N, T, VT> не будет совпадать с произвольным T. Таким образом, шаблон вашего собственного решения является более специализированным и вызывается, а затем явно пересылается на другой шаблон.

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


Спрашивающий впоследствии отредактировал свой собственный ответ, поэтому большинство моих ссылок выше на «ваш собственный ответ» больше не применяются.

0 голосов
/ 08 сентября 2010
template<typename T1, **typename T2**>
inline T1 min(const T1& k1, **const T2&** k2) {
    return k1 < k2 ? k1 : k2;
}

...

template<int N, typename T>
struct vec { 
    typedef container<N,T,vec_array<N,T> > t;
};


...

vec<2,float>::t v1,v2;
min(v1,v2);

Вот что я наконец-то сделал, чтобы заставить его работать.

Неоднозначность состояла в том, что оба аргумента имеют один и тот же тип - container<2,float,vec_array<2,float> >. Это одно очко для метода min(const T&,const T&). Так как min(const container<N,T,VT1>& v1, const container<N,T,VT2>& v2) соответствует и более специализирован, он также получил дополнительное очко, и компилятор не мог решить, какой из них использовать. Переключение универсального min на использование двух аргументов типа - min (const T1 &, const T2 &) - превосходит его в представлении.

Я также переключился на использование «определения типа шаблона» вместо наследования для определения vec<N,T> без необходимости разбираться с беспорядочными вещами container<N,T,VT>. Это делает vec<N,T>::t точным соответствием правильной функции.

Теперь, когда я использую typedef, а не наследование и два типа в общей функции min вместо одного, вызывается правильный метод.

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