从具有公共基(C++)的函数对象进行乘法继承

Multiply inheriting from function objects with a common base (C++)

本文关键字:对象 函数 继承 C++      更新时间:2023-10-16

我有一些函数对象没有成员变量。函数对象本质上非常简单。它们都继承自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 2008projectsdefaulttemplateargumentsmain.cpp    12

现在一般来说,在多重继承中,这是通过虚拟继承来解决的;在这种情况下,我想避免这种情况。在这种情况下,我能做些什么来删除警告?

我的直接解决方案是不从binary_function<>继承,因为我假设key_to_hash_method将是binary_function。这个解决方案感觉有点像一个程序员,他无权包含guards或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("%in", f.test());
}

您的hash_shrink_method_1不需要直接从binary_function继承,因为您假设其参数类key_to_hash_method已经继承了。如果您想确定,可以添加一个静态断言(std::is_base_of);不过,如果您已经有了C++11,那么无论如何都可以去掉过时的binary_function

语言律师部分

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

如果这不是您想要的,那么您不应该在这里使用公共继承,也不应该在一般情况下使用继承。这里没有继承的理由。

我看到了两种非常好的方法。

你已经提到的第一个:作文。这很简单,代码看起来也很自然。

然而,从示例的运行方式来看,代码甚至不需要实例化任何这些函数对象。因此,您可以更改类,使其具有与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