Многократное наследование от функциональных объектов с общей базой (C ++) - PullRequest
3 голосов
/ 24 января 2012

У меня есть несколько функциональных объектов, которые не имеют переменных-членов. Функциональные объекты очень просты по своей природе. Все они наследуются от 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 ]

2 голосов
/ 24 января 2012

Ваш hash_shrink_method_1 не должен наследоваться от binary_function напрямую, поскольку вы предполагаете, что его класс параметров key_to_hash_method уже делает это. Вы можете добавить статическое утверждение (std::is_base_of), если хотите быть уверенным; хотя, если у вас уже есть C ++ 11, вы все равно можете покончить с устаревшим binary_function.

1 голос
/ 24 января 2012

Языковая часть юристов.

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

Если это не то, что вы хотите, вам не следует использовать публичное наследование здесь или, возможно, наследование в целом. Здесь нет причин для наследования.

0 голосов
/ 24 января 2012

Я вижу два довольно хороших подхода.

Первое, что вы уже упомянули: композиция. Это просто сделать, и код выглядит вполне естественно.

Однако, как работают ваши примеры, похоже, что вашему коду не нужно даже создавать экземпляры этих функциональных объектов. Таким образом, вы можете изменить классы на статический метод, который делает то же самое, что и 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...