Предотвращение дублирования определений функций в специализациях шаблонов - PullRequest
10 голосов
/ 19 августа 2011

В классе Widget есть некоторые функции, которые применяются для всех типов параметров (общие функции) и другие функции, которые должны быть специализированы для данных типов (необычные функции).

g ++ настаивает на том, чтобы специализация для Widgetтакже определите 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

в Alf:

Я не могу использовать

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

to iammilind:

Это интересно, но я не могу использовать деривацию из Widget (или, возможно, более ясное решение рефакторинга общих частей в родительский класс GeneralWidget) по той же причине.Общие части не совсем общие.Их декларации и их определения выглядят одинаково, но поскольку они используют черты, в конце они совершенно разные.

end-edit2

Ответы [ 4 ]

7 голосов
/ 19 августа 2011
#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 );
}
1 голос
/ 19 августа 2011

Во-первых, это не совсем поддерживаемая вещь в C ++. К сожалению, специализация шаблона требует переопределения всех методов.

Вы можете поместить свои общие методы в базовый класс, а затем включить их по наследству, хотя мой собственный опыт с этим был смешанным (функция в родительском классе просто не ведет себя так же, как функция в дочернем классе во всех случаях, дружба работает странно, и если базовый класс также является шаблоном, вы должны полностью указать его всякий раз, когда вы хотите использовать любой из его членов [кроме компиляторов MS]. Операторы никогда не работают так, как ну тоже). Вы можете сделать это, скопировав ваши обычные методы, а это именно то, чего вы пытаетесь избежать. Или, в качестве компромисса по вставке копий, вы можете попросить компилятор сделать вставку для вас:

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, я полагаю. Это, безусловно, злоупотребление препроцессором, но оно делает то, о чем вы просите, избегает головной боли при наследовании шаблонов (а они действительно весьма болезненны) и позволяет вам хранить только одну копию вашего общего кода. И если кто-то хочет критиковать, что это за ужасный клуг, сделайте это с помощью улучшенного решения;).

0 голосов
/ 11 января 2013

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

Вместо того, чтобы пытаться извлечь необычную функциональность, вы можете извлечь общую функциональность всобственный класс, а затем предоставить функции-оболочки в специализациях классов.Любые конструкторы или деструкторы по умолчанию даже не нужно переносить!

#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 );
}

В этом случае это увеличивает объем кода, но общая реализация существует только один раз.Создание большего количества специализаций потребует только дублирования шаблона обертки, а не поведенческого кода.В реальном мире, где может быть сотни строк общего кода, такой подход позволяет избежать большого количества дублирования.

0 голосов
/ 19 августа 2011

В идеале, в случае специализации class, мы предполагаем специализировать все необходимые методы.Если вы этого не хотите, то работает следующий трюк (с некоторыми ограничениями):

template<>
struct Widget<char> : Widget<int>
{
  //...
};

Мне ставят <int> для простоты, которая считается не специализированной (вы можете поставить что-токак Widget<void**>, чтобы быть на более безопасной стороне).

assert( WidgetChar.common_fn() == 'a' ); // ok
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...