使用公共库(C ++)从函数对象继承

时间:2012-01-24 05:31:39

标签: c++ templates multiple-inheritance

我有一些没有成员变量的函数对象。功能对象本质上非常简单。它们都继承自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());
}

3 个答案:

答案 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;

OOP / OOD律师部分。

但请注意,您的班级现在可以公开访问两个 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