【问题标题】:Multiply inheriting from function objects with a common base (C++)乘以从具有公共基础的函数对象继承 (C++)
【发布时间】:2012-01-24 05:31:38
【问题描述】:

我有一些没有成员变量的函数对象。函数对象本质上非常简单。它们都继承自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&lt;int, int, int&gt; 会被继承两次,从而产生警告(在 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&lt;&gt; 继承,因为我假设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());
}

【问题讨论】:

    标签: c++ templates multiple-inheritance


    【解决方案1】:

    您的hash_shrink_method_1 不需要直接从binary_function 继承,因为您假设它的参数类key_to_hash_method 已经这样做了。如果您想确定,可以添加静态断言 (std::is_base_of);不过,如果您已经拥有 C++11,则无论如何都可以取消过时的 binary_function

    【讨论】:

    • 虽然我们还没有使用C++11,但我们很快就会切换。我不知道 binary_function 已被弃用。它的替代品是什么?
    • @Samaursa:没什么,多亏了可变参数模板,它不再需要了。
    • 还有一个相关问题。假设我有各种函数对象 f1、f2、f3、f4、f5、f6。作为一个通用模板类,我可以接受用户选择的几个(比如说 4 个)一元或二元函数对象。我从函数对象继承以避免浪费空间。作为泛型模板类,我不知道每个函数对象都继承了什么。 f1 可能从 f2 继承,因为它需要访问 f2 的功能来计算某些东西。泛型模板类有没有办法表明它不关心同一个类被继承两次?
    【解决方案2】:

    语言律师部分。

    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
    

    如果这不是你想要的,你不应该在这里使用公共继承或者一般的继承。这里没有继承的理由。

    【讨论】:

    • 为了消除歧义,我使用 typedef 来提升层次结构。 (例如 base_type::base_type::operator() )。这是不标准的行为吗?另外,如果我使用组合,我正在继承以删除空类占用的额外空间。
    • (1 -- disambiguation) 我认为这确实是非标准的。 (2——继承)在这种情况下,您可以使用私有或受保护的继承(这与组合足够接近)。保留公共继承来表达类之间的 is-a 关系。
    【解决方案3】:

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

    您已经提到的第一个:组合。这样做很简单,代码看起来也很自然。

    但是,从您的示例运行方式来看,您的代码似乎甚至不需要实例化任何这些函数对象。因此,您可以将类更改为具有与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 可能是一个想法。

    【讨论】:

    • @BjörnPollex:谢谢!我会解决的!
    猜你喜欢
    • 2012-11-27
    • 2016-08-10
    • 2023-02-22
    • 1970-01-01
    • 2017-12-29
    • 1970-01-01
    • 1970-01-01
    • 2011-03-03
    • 1970-01-01
    相关资源
    最近更新 更多