Предотвращение дублирования определений функций в специализациях шаблонов
виджет класс имеет некоторые функции, которые применяются для всех типов параметров (общие функции) и другие функции, которые должны быть специализированы для заданных типов (необычные функции).
g++ настаивает на том, что специализация виджета должна также определять common_fn (), а не только uncommon_fn (), но это в первую очередь противоречит цели использования специализации. Как можно избежать повторения common_fn()?
#include <cassert>
template<typename Type> struct Widget
{
Widget() {}
char common_fn() { return 'a'; }
int uncommon_fn() { return 1; }
};
template<> struct Widget<char>
{
Widget() {}
int uncommon_fn() { return 2; }
};
int main()
{
Widget<char> WidgetChar;
assert( WidgetChar.common_fn() == 'a' ); // Error
assert( WidgetChar.uncommon_fn() == 2 );
}
begin-edit
Альф:
Я не могу использовать
template<> int Widget<char>::uncommon_fn() { return 2; }
потому что некоторые из необычных функций должны возвращать тип признака (и поэтому было чрезмерно упрощать, делая фактический примитив типа).
или на самом деле есть способ заставить компилятор распознавать typename Foo::Bar
когда пишу
struct Foo { typedef FooBar Bar; };
template<> typename Foo::Bar Widget<Foo>::uncommon_fn() { return ....; }
?
end-edit
begin-edit2
в iammilind:
Это интересно, но я не могу использовать вывод из виджета (или, возможно, более четкое решение рефакторинга общих частей в родительский класс GeneralWidget) по той же причине. Общие части не совсем общие. Их объявления и их определения выглядят одинаково, но поскольку они используют черты, они в конце совсем другое.
end-edit2
4 ответов
#include <assert.h>
template<typename Type> struct Widget
{
Widget() {}
char common_fn() { return 'a'; }
int uncommon_fn() { return 1; }
};
template<>
int Widget<char>::uncommon_fn() { return 2; }
int main()
{
Widget<char> WidgetChar;
assert( WidgetChar.common_fn() == 'a' ); // OK
assert( WidgetChar.uncommon_fn() == 2 );
}
во-первых, это на самом деле не поддерживается в C++. Специализация шаблона требует от вас переопределить все методы, к сожалению.
вы можете поместить свои общие методы в базовый класс, а затем включить их по наследству, хотя мой собственный опыт с этим был смешанным мешком (функция в родительском классе просто не ведет себя так же, как функция в дочернем классе во всех случаях; дружба работает странно, и если базовый класс это также шаблон, вы должны полностью указать его, когда вы хотите использовать любой из его членов [кроме MS compilers]. Операторы тоже никогда не работают так хорошо). Вы можете сделать это, скопировав свои общие методы, чего именно вы пытаетесь избежать. Или, в качестве компромисса при копировании-вставке, вы можете попросить компилятор сделать вашу копию-вставку для вас:
template<> struct Widget<char>
{
#include "Widget_common.hpart"
int uncommon_fn() { return 2; }
};
затем вы создаете файл под названием "Widget_common.hpart", который содержит
// This file contains common functions for the Widget<> template, and will be #included
// directly into that template; it should not be included as a standalone header.
char common_fn() { return 'a'; }
вы также можете просто использовать норматив.расширение h, я полагаю. Это определенно злоупотребление препроцессором, но он делает то, что вы просите, избегает головных болей наследования шаблонов (и они действительно довольно болезненны) и позволяет вам хранить только одну копию вашего общего кода. И если кто-то хочет критиковать, какой ужасный kludge это, сделайте это с улучшенным решением ;).
в идеале, в случае class
специализация, мы должны специализировать все необходимые методы. Если вы этого не хотите, то работает следующий трюк (с определенными ограничениями):
template<>
struct Widget<char> : Widget<int>
{
//...
};
Я <int>
для простоты, которая, как предполагается, не является специализированной (вы можете поставить что-то вроде Widget<void**>
чтобы быть на безопасной стороне).
assert( WidgetChar.common_fn() == 'a' ); // ok
Я посмотрел на ваш другой пост относительно черт, но увидел, что вы, похоже, решили свою проблему, решив не использовать специализацию. Но для тех, у кого была такая же проблема, у меня есть другое решение, которое люди могут захотеть рассмотреть.
вместо того, чтобы пытаться извлечь необычную функциональность, вы можете извлечь общую функциональность в собственный класс, а затем предоставить функции-оболочки в специализации класса. Любые конструкторы или деструкторы по умолчанию даже не должны быть обернуты!
#include <cassert>
template<typename Type> struct WidgetCommon
{
char common_fn() { return 'a'; } // 1,000,000 line function here :D
};
template<typename Type> struct Widget
{
Widget() {}
char common_fn() { return common.common_fn(); }
int uncommon_fn() { return 1; }
private:
WidgetCommon<Type> common;
};
template<> struct Widget<char>
{
Widget() {}
char common_fn() { return common.common_fn(); }
int uncommon_fn() { return 2; }
private:
WidgetCommon<char> common;
};
int main()
{
Widget<char> WidgetChar;
assert( WidgetChar.common_fn() == 'a' );
assert( WidgetChar.uncommon_fn() == 2 );
Widget<int> WidgetInt;
assert( WidgetInt.common_fn() == 'a' );
assert( WidgetInt.uncommon_fn() == 1 );
}
в этом случае он увеличивает объем кода, но общая реализация существует только один раз. Создание большего количества специализаций потребует только дублирования шаблона оболочки, а не поведенческого кода. В реальном мире, где могут быть сотни строк общего кода, такой подход может избежать большого дублирования.