问题
I have a few function objects that have no member variables. The function objects are very simple in nature. They all inherit from unary_function<>
or binary_function<>
. For example, a couple of the function objects may be something like this:
struct key_to_hash_method_1 : public binary_function<int, int, int>
{
int operator() (int a, int b) const { /* do something */ }
};
template <typename key_to_hash_method>
struct hash_shrink_method_1 : public binary_function<int, int, int>, public key_to_hash_method
{
int operator() (int a, int b) const { /* do something while utilizing key_to_hash_method */ }
};
/* and more variations of these function objects */
A template class uses these function objects by taking them as template parameters as policies. The template class then inherits from them:
template <typename hash_method>
class foo : public hash_method
{
public:
/* do something while using hash_method as well as using the information provided by binary_function<> to selective compile different functions*/
};
Of course, in the interest of keeping the example simple, the above may not make much sense as far as usefulness goes.
Why am I inheriting instead of using composition? Simply to avoid the empty classes from taking up space. Whether the space saved is minuscule or not is not the point of the question.
As you can see from the above code, binary_function<int, int, int>
will be inherited twice, which gives rise to the warning (in VC++ 2008):
Warning 1 warning C4584: 'hash_shrink_method_1<key_to_hash_method>' : base-class 'std::binary_function<_Arg1,_Arg2,_Result>' is already a base-class of 'key_to_hash_method_1' c:\visual studio 2008\projects\defaulttemplatearguments\main.cpp 12
Now generally, in multiple inheritance, this is solved by virtual inheritance; which I want to avoid in this case. What can I do in this situation to remove the warning?
My immediate solution is to not inherit from binary_function<>
since I am assuming that key_to_hash_method
will be a binary_function
. This solution feels a bit like a programmer who does not have access to include guards or pragma once
statement. Yes, he can avoid including the header twice, but he'd rather the compiler figure it out for him. I would like the same in this case.
Example code, if you want to try it out:
#include <functional>
using namespace std;
struct key_to_hash_method_1 : public binary_function<int, int, int>
{
int operator() (int a, int b) const { return a + b; }
};
template <typename key_to_hash_method>
struct hash_shrink_method_1 : public binary_function<int, int, int>, public key_to_hash_method
{
int operator() (int a, int b) const { return key_to_hash_method::operator()(1, 2) * 5; }
};
template <typename hash_method>
class foo : public hash_method
{
public:
int test()
{
/* in actual code, this function selectively calls other functions
depending on whether hash_method is unary or binary */
return hash_method::operator()(5, 6);
}
};
int main()
{
foo<hash_shrink_method_1<key_to_hash_method_1> > f;
printf("%i\n", f.test());
}
回答1:
Your hash_shrink_method_1
doesn't need to inherit from binary_function
directly, since you assume that its parameter class key_to_hash_method
already does so. You can add a static assertion (std::is_base_of
) if you want to be sure; though if you already have C++11, you can do away with the obsolete binary_function
anyway.
回答2:
Language lawyering part.
GCC gives a better warning:
mi.C: In instantiation of ‘hash_shrink_method_1<key_to_hash_method_1>’:
mi.C:18: instantiated from ‘foo<hash_shrink_method_1<key_to_hash_method_1> >’
mi.C:30: instantiated from here
mi.C:12: warning: direct base ‘std::binary_function<int, int, int>’ inaccessible in ‘hash_shrink_method_1<key_to_hash_method_1>’ due to ambiguity
Which is to say, if you inherit from the same base both directly and indirectly, the direct base class cannot be accessed. An attempt to do so will be a compile-time error.
hash_shrink_method_1<key_to_hash_method_1> foo;
binary_function<int, int, int>& bar = foo; // error: ambiguous
There's no way to disambiguate that.
The obvious solution, apart from using virtual inheritance, would be introducing an intermediate layer of inheritance.
template <typename T>
struct wrapped : public T {};
and then
template <typename key_to_hash_method>
struct hash_shrink_method_1 : public wrapped < binary_function<int, int, int> >,
public wrapped <key_to_hash_method >
Now it is possible to disambiguate:
hash_shrink_method_1<key_to_hash_method_1> foo;
foo::wrapped<binary_function<int, int, int> >& intermediate = foo;
binary_function<int, int, int>& bar = intermediate;
OOP/OOD lawyering part.
Please note however that your class now has two operator()(int,int)
functions publicly accessible. Which one will be selected depends on how do you access them.
foo<hash_shrink_method_1<key_to_hash_method_1> > f;
hash_shrink_method_1<key_to_hash_method_1>& ff = f;
cout << ff(5,6) << endl; // 15
key_to_hash_method_1& gg = ff;
cout << gg(5,6) << endl; // 11
If this is not what you want, you should not use public inheritance here or perhaps inheritance in general. There's no reason fot inheritance here.
回答3:
I see two pretty nice approaches.
The first you already mentioned: composition. This is simple to do, and the code looks quite natural.
However, the way your examples run, it looks like your code shouldn't even need to instantiate any these function objects. So you could change the classes to have a static method which does the same thing as operator()
, e.g.
struct key_to_hash_method_1 : public binary_function<int, int, int>
{
static int invoke (int a, int b) { return a + b; }
int operator() (int a, int b) const { return a + b; }
};
template <typename key_to_hash_method>
struct hash_shrink_method_1 : public binary_function<int, int, int>
{
static int invoke (int a, int b) { return key_to_hash_method::invoke(1, 2) * 5 }
int operator() (int a, int b) const { return key_to_hash_method::invoke(1, 2) * 5; }
};
Notice that hash_shrink_method_1
doesn't need to inherit from key_to_hash_method_1
any more. Similarly, foo
won't need to inherit from hash_method
.
(An alternative to using these static methods is to implement the singleton pattern)
A last thought occured to me: since you're using templates to invoke the methods, you don't really need to use binary_function
(at least as far as these examples go). If your actual situation is similar to your example code, it might be an idea to just get rid of binary_function
.
来源:https://stackoverflow.com/questions/8982430/multiply-inheriting-from-function-objects-with-a-common-base-c