我有一些没有成员变量的函数对象。功能对象本质上非常简单。它们都继承自unary_function<>
或binary_function<>
。例如,一些函数对象可能是这样的:
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 */
模板类使用这些函数对象作为模板参数作为策略。然后模板类继承它们:
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*/
};
当然,为了保持示例简单,就有用性而言,上述内容可能没什么意义。
为什么我继承而不是使用合成?只是为了避免空类占用空间。节省的空间是否微不足道并不是问题的关键。
从上面的代码中可以看出,binary_function<int, int, int>
将被继承两次,从而产生警告(在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
现在一般来说,在多重继承中,这是通过虚拟继承来解决的;在这种情况下我想避免的。在这种情况下我可以做些什么来删除警告?
我的直接解决方案是不继承binary_function<>
,因为我假设key_to_hash_method
将是binary_function
。这个解决方案感觉有点像程序员无法访问包含警卫或pragma once
语句。是的,他可以避免两次包括标题,但他宁愿编译器为他弄清楚。在这种情况下,我也希望如此。
示例代码 ,如果您想尝试一下:
#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());
}
答案 0 :(得分:2)
您的hash_shrink_method_1
不需要直接从binary_function
继承,因为您认为其参数类key_to_hash_method
已经这样做了。如果您想确定,可以添加静态断言(std::is_base_of
);虽然如果你已经拥有C ++ 11,你仍然可以废除过时的binary_function
。
答案 1 :(得分:1)
GCC提供了更好的警告:
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
也就是说,如果直接或间接地从同一个基础继承,则无法访问直接基类。尝试这样做将是编译时错误。
hash_shrink_method_1<key_to_hash_method_1> foo;
binary_function<int, int, int>& bar = foo; // error: ambiguous
没有办法消除歧义。
除了使用虚拟继承之外,显而易见的解决方案是引入一个中间继承层。
template <typename T>
struct wrapped : public T {};
然后
template <typename key_to_hash_method>
struct hash_shrink_method_1 : public wrapped < binary_function<int, int, int> >,
public wrapped <key_to_hash_method >
现在可以消除歧义:
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;
但请注意,您的班级现在可以公开访问两个 operator()(int,int)
个功能。选择哪一个取决于您如何访问它们。
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
如果这不是您想要的,那么您不应该在这里使用公共继承,也不应该使用继承。这里没有理由继承。
答案 2 :(得分:0)
我看到两种非常好的方法。
你已经提到过的第一个:作文。这很简单,代码看起来很自然。
但是,您的示例运行方式,看起来您的代码甚至不需要实例化任何这些函数对象。因此,您可以将类更改为具有与operator()
完全相同的静态方法,例如
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; }
};
请注意,hash_shrink_method_1
不再需要继承key_to_hash_method_1
。同样,foo
也不需要继承hash_method
。
(使用这些静态方法的另一种方法是实现单例模式)
最后一个想法发生在我身上:因为你正在使用模板来调用方法,所以你真的不需要使用binary_function
(至少就这些例子而言)。如果您的实际情况与示例代码类似,那么可能只想摆脱binary_function
。