Специализированный одиночный метод в большом классе шаблона
В С++, если вы хотите частично специализировать один метод в классе шаблона, вам нужно специализировать весь класс (как указано, например, в Специализация шаблона одного метода из шаблонного класса с несколькими параметрами шаблона)
Это, однако, становится утомительным в более крупных классах шаблонов с несколькими параметрами шаблона, когда каждый из них влияет на одну функцию. С параметрами N вам нужно специализировать класс 2 ^ N раз!
Однако, с С++ 11, я думаю, что может быть более элегантное решение, но я не уверен, как подойти к нему. Возможно, как-то с enable_if
? Любые идеи?
Ответы
Ответ 1
В дополнение к решению на основе наследования, предложенному Torsten, вы можете использовать std::enable_if
и параметры шаблона по умолчанию для включения/отключения определенных специализация функции.
Например:
template<typename T>
struct comparer
{
template<typename U = T ,
typename std::enable_if<std::is_floating_point<U>::value>::type* = nullptr>
bool operator()( U lhs , U rhs )
{
return /* floating-point precision aware comparison */;
}
template<typename U = T ,
typename std::enable_if<!std::is_floating_point<U>::value>::type* = nullptr>
bool operator()( U lhs , U rhs )
{
return lhs == rhs;
}
};
Мы используем SFINAE, чтобы отключить/включить различные "специализации" функции в зависимости от параметра шаблона. Поскольку SFINAE может зависеть только от параметров функции, а не от параметров класса, нам нужен необязательный параметр шаблона для функции, которая принимает параметр класса.
Я предпочитаю это решение над наследованием, потому что:
- Требуется меньше набирать. Меньшая типизация, вероятно, приводит к меньшим ошибкам.
- Все специализации записываются внутри класса. Таким образом, для написания специализаций сохраняются все специализации внутри исходного класса и делается специализация похожей на функциональные перегрузки, а не на сложный код на основе шаблонов.
Но с компиляторами, которые не реализовали необязательные параметры шаблона функции (например, MSVC в VS2012), это решение не работает, и вы должны использовать решение наследования.
РЕДАКТИРОВАТЬ:. Вы можете проезжать по параметрам-не-реализованным параметрам-функции-шаблона, которые обертывают функцию шаблона другой функцией, которая делегирует работу:
template<typename T>
struct foo
{
private:
template<typename U>
void f()
{
...
}
public:
void g()
{
f<T>();
}
};
Конечно, компилятор может легко встроить g()
, выкидывая вызов обертывания, поэтому на эту альтернативу не влияет производительность.
Ответ 2
Одним из решений было бы перейти от функции, которую вы хотите перегрузить для некоторой реализации, которая зависит от аргументов шаблона классов:
template < typename T >
struct foo {
void f();
};
template < typename T >
struct f_impl {
static void impl()
{
// default implementation
}
};
template <>
struct f_impl<int> {
static void impl()
{
// special int implementation
}
};
template < typename T >
void foo< T >::f()
{
f_impl< T >::impl();
}
Или просто используйте частные функции, вызывайте их с параметром шаблона и перегружайте их.
template < typename T >
class foo {
public:
void f()
{
impl(T());
}
private:
template < typename G >
void impl( const G& );
void impl( int );
};
Или, если это действительно только одна особая ситуация с очень специальным типом, просто запрос для этого типа в реализации.
Ответ 3
С enable_if:
#include <iostream>
#include <type_traits>
template <typename T>
class A {
private:
template <typename U>
static typename std::enable_if<std::is_same<U, char>::value, char>::type
g() {
std::cout << "char\n";
return char();
}
template <typename U>
static typename std::enable_if<std::is_same<U, int>::value, int>::type
g() {
std::cout << "int\n";
return int();
}
public:
static T f() { return g<T>(); }
};
int main(void)
{
A<char>::f();
A<int>::f();
// error: no matching function for call to ‘A<double>::g()’
// A<double>::f();
return 0;
}
Ответ 4
Отправка тегов часто является чистым способом для этого.
В базовом методе используйте класс признаков, чтобы определить, какую суб-версию метода вы хотите вызвать. Это генерирует тип (называемый тегом), который описывает результат решения.
Затем перейдите к этой дополнительной версии, передающей экземпляр типа тега. Разрешение перегрузки срабатывает, и только созданное вами приложение получает экземпляр и вызывается.
Разрешение перегрузки на основе типа параметра - гораздо менее безумный способ обработки отправки, поскольку enable_if
является хрупким, сложным в точке использования, становится действительно сложным, если у вас есть 3+ перегрузки, и есть странные угловые случаи что может удивить вас замечательными ошибками компиляции.
Ответ 5
Возможно, я ошибаюсь, но выбран лучший кандидат от Manu343726 имеет ошибку и не будет компилироваться. Обе перегрузки операторов имеют ту же подпись. Рассмотрите лучший вопрос, о котором идет речь std:: enable_if: параметр vs template
P.S. я бы добавил комментарий, но недостаточно репутации, извините