问题
The class Widget has some functions that apply for all parameter types (common functions) and other functions that need to be specialized for given types (the uncommon functions).
g++ insists that the specialization for Widget should also define common_fn() and not just uncommon_fn(), but that defeats the purpose of using specialization in the first place. How can one avoid repeating 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
to Alf:
I am unable to use
template<> int Widget<char>::uncommon_fn() { return 2; }
because some of the uncommon functions need to return a trait type (and so it was excessive to simplify by making the actual type primitive).
Or is there in fact a way to make the compiler recognize typename Foo::Bar
when writing
struct Foo { typedef FooBar Bar; };
template<> typename Foo::Bar Widget<Foo>::uncommon_fn() { return ....; }
?
end-edit
begin-edit2
to iammilind:
That's interesting, but I am unable to use derivation from Widget (or the possibly clearer solution of refactoring the common parts into a parent class GeneralWidget) for the same reason. The common parts are not entirely common. Their declarations and their definitions look the same, but because they use traits they are at the end quite different.
end-edit2
回答1:
#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 );
}
回答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 ;).
回答3:
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
回答4:
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.
来源:https://stackoverflow.com/questions/7116610/avoiding-duplication-of-function-definitions-in-template-specializations