Avoiding duplication of function definitions in template specializations

落爺英雄遲暮 提交于 2019-12-05 02:42:25
Cheers and hth. - Alf
#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 );
}

First off, this isn't really a supported thing to do in C++. Template specialization requires you to redefine all the methods, unfortunately.

You can put your common methods in a base class, and then include them by inheritance, although my own experience with that has been a mixed bag (a function in the parent class just doesn't behave quite the same way as a function in the child class in all cases; friendship works strangely, and if the base class is also a template, you have to fully specify it whenever you wish to use any of its members [except on MS compilers]. Operators never work quite as well either). You can do it by copy-pasting your common methods, which is precisely what you're trying to avoid. Or, as a compromise on copy-pasting, you can have the compiler do your copy-pasting for you:

template<> struct Widget<char>
{
    #include "Widget_common.hpart"
    int uncommon_fn() { return 2; }
};

Then you make a file called "Widget_common.hpart" that contains

// 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'; }

You can also just use the standard .h extension, I suppose. This is definitely preprocessor abuse, but it does what you ask for, avoids the template inheritance headaches (and they really are quite painful), and lets you keep only a single copy of your common code. And if anyone wants to criticize what a horrible kludge this is, do so with an improved solution ;).

Ideally, in the case of class specialization, we are suppose to specialize all the required methods. If you don't want that, then following trick works (with certain limitations):

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

I am put <int> for simplicity which is assumed to be not specialized (you can put something like Widget<void**> to be on safer side).

assert( WidgetChar.common_fn() == 'a' ); // ok

I did take a look at your other post regarding traits, but saw that you seem to have solved your problem by deciding not to use specialization. But, for the benefit of others who have had the same problem, I have another solution that people might want to consider.

Instead of trying to extract the uncommon functionality, you can extract the common functionality into a class of its own, and then provide wrapper functions in the class specializations. Any default constructors or destructors don't even need to be wrapped!

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

In this case, it does increase the amount of code, but the common implementation only exists once. Creating more specializations will only require duplicating the wrapper boilerplate rather than the behavioural code. In the real world, where there may be hundreds of lines of common code, this approach can avoid a lot of duplication.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!