#c #templates
#c #шаблоны
Вопрос:
У меня есть класс
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
}
Когда T=int
, W=int
, или W=T
, этот класс не может скомпилироваться. Как я могу заставить методы иметь приоритет друг над другом?
Мне нужен приоритет W > T > int
. Поэтому, если W=T
, foo(T)
игнорируется и foo(W)
вызывается. Если T=int
, foo(int)
игнорируется и foo(T)
вызывается.
Компилятор — VS2012, но у меня тоже есть Linux, и я также рассмотрю решения GCC / Clang. Все, что компилируется на любом основном компиляторе, идет, но только если вы укажете, на каких компиляторах оно работает.
Комментарии:
1. Похоже, задание для
std::enable_if
.2. @Deduplicator Я думаю, что в случае
enable_if
сбоя класс не сможет скомпилироваться.3. Я почти не решаюсь спрашивать, но мое любопытство часто является моим врагом; Почему ? Для чего это используется?
4.
std::enable_if<std::is_same<A, B>::value, A>::type
этот тип компилируется, и он действителен, только еслиA
иB
совпадают. Вы можете адаптировать его к своим потребностям. en.cppreference.com/w/cpp/types/enable_if вы также должны указать версию C , на которую вы ориентируетесь.5.
enable_if
при использовании в функции-члене может работать только с параметрами шаблона этой функции-члена (а не с параметрами шаблона шаблона класса, содержащего эту функцию-член). Поэтому я не понимаю, как это будет работать здесь.
Ответ №1:
Я бы отметил dispatch . Переопределение диспетчеризации легко понять и масштабируется.
Мы начинаем с идеальной пересылки:
template<class U> void foo(Uamp;amp;u){
foo( std::forward<U>(u), std::is_convertible<U, W>{}, std::is_convertible<U,T>{} );
}
он создает типы тегов, в данном случае типы true или false, для отправки.
Этот:
void foo( W, std::true_type, ... );
улавливает все, что может быть преобразовано в W.
Далее мы блокируем этот:
void foo( T, std::false_type, std::true_type );
из рассмотренных случаев, когда первый аргумент может преобразовываться в W
.
Наконец, этот:
void foo( int, std::false_type, std::false_type );
может рассматриваться только в том случае, если первый параметр не может быть преобразован ни в один из них.
Возможны более причудливые типы тегов или выполнение отправки по одному за раз.
Извините за опечатки.
Я использую одну функцию C 11 — {}
для создания объекта — выше. Если вашему компилятору не хватает поддержки этой функции C 11, просто обновите свой компилятор, это 2014 год, смирись с этим. В противном случае замените {}
на ()
.
Комментарии:
1. Вау! Приятно! Я понятия не имел об этом шаблоне. Попробую.
2. О каком хите производительности мы говорим? Может ли компилятор разрешить это время компиляции?
3. @pythonnut Да. Это
true_type
типы, следовательно, время компиляции. Пересылка — это не что иное, как приведение ссылок (noop). Вы можете столкнуться с сбоем вашего оптимизатора, потому что он отказывается без уважительной причины, поэтому, если это очень важно, profile, но на самом деле ничего не делается, и этот метод широко используетсяstd
.4. Я что-то упускаю, или ваши паренты отправки не совпадают?
5. вероятно, @pythonnut. Дизайн хороший, но мой компьютер отказался от ghost, поэтому не тестировался. 😉 Исправлена
)
проблема <->>
.is_convertible
может быть написано неправильно, аргументы типа также могут быть обратными. Ах...
, тоже ложный. Теперь можно компилировать!
Ответ №2:
Используйте std::enable_if
:
#include <type_traits>
template <typename T, typename W>
struct A {
void foo(W) {}
template<typename XT=T> typename std::enable_if<std::is_same<XT,T>::value
amp;amp; !std::is_same<T, W>::value, void>::type foo(T) {}
template<typename XT=int> typename std::enable_if<std::is_same<XT,int>::value
amp;amp; !std::is_same<int, T>::value
amp;amp; !std::is_same<int, W>::value, void>::type foo(int) {}
};
Добавлено для тестирования:
template struct A<short,char>;
template struct A<char,char>;
template struct A<char,int>;
template struct A<int,char>;
template struct A<int, int>;
struct S {};
int main() {
A<S, int>{}.foo(S{});
}
Комментарии:
1. Это не работает для меня (g -4.9); по-видимому, 0 не может быть преобразован в значение enable_if::type * . Это ошибка в gcc? (В любом случае, он работает с
nullptr
вместо0
)2. Также не будет компилироваться в Visual Studio, поскольку он не поддерживает аргументы шаблона по умолчанию для элементов.
3. @Брайан: Изменен и протестирован повторно.
Ответ №3:
Для соответствующей части вашего шаблона вы можете использовать спецификации:
template <typename U, typename W>
struct Foo
{
void f(U);
void f(W);
};
template <typename T>
struct Foo<T, T>
{
void f(T);
};
Для остальной части вашего класса или шаблона класса вы можете наследовать Foo<A, B>
, чтобы исключить общий код из части, которая нуждается в специализации:
template <typename A, typename B>
struct TheClass : Foo<A, B>
{
// common code
};
Комментарии:
1. Понадобилось бы 3 специализации, плюс базовая версия!
Ответ №4:
Попробуйте специализации шаблонов:
template <typename T, typename W>
class A {
void foo(W);
void foo(T);
void foo(int);
};
template <typename T>
class A<T, T> {
void foo(T);
void foo(int);
};
template <>
class A<int, int> {
void foo(int);
};
Комментарии:
1. Потребуется 3 специализации плюс базовая версия!
2. Кроме того, вы должны дублировать оставшуюся часть содержимого класса для каждой специализации!
3. @Matt McNabb нет, если вы наследуете, что предлагают другие.
Ответ №5:
Вот решение без специализации A, но с двумя вспомогательными структурами в нескольких формах.
#include <iostream>
template<typename T, typename W>
struct T_type { typedef T type; };
template<typename W>
struct T_type<W, W> { typedef void* type; /*dummy type*/};
template<typename T, typename W>
struct int_type { typedef int type; };
template<typename W>
struct int_type<int, W> { typedef void** type; /*dummy type*/};
template<typename T>
struct int_type<T, int> { typedef void** type; /*dummy type*/};
template<>
struct int_type<int, int> { typedef void** type; /*dummy type*/};
template<typename T, typename W>
class A {
public:
void foo(W w) {
std::cout << "foo(W)" << std::endl;
}
void foo(typename T_type<T, W>::type t) {
std::cout << "foo(T)" << std::endl;
}
void foo(typename int_type<T, W>::type i) {
std::cout << "foo(int)" << std::endl;
}
};
int main() {
std::cout << "A<float, char>" << std::endl;
A<float, char> a;
a.foo(1.0f);
a.foo('1');
a.foo(1);
std::cout << "A<float, float>" << std::endl;
A<float, float> b;
b.foo(1.0f);
b.foo(1);
std::cout << "A<int, int>" << std::endl;
A<int, int> c;
c.foo(1);
return 0;
}