Может ли шаблон C ++ сделать это для условий улучшения кода? - PullRequest
2 голосов
/ 09 января 2009

Я хочу сделать это:
func (условие A? Pa1: pa2, условие B? Pb1: pb2, условие C? Pc1: pc2);

В функции стиля C проблем нет. Но если func () является функцией шаблона, компилятор сообщит об ошибках. Здесь pa1 и pa2, ... - разные классы и имеют static метод - "convert ()". convert () также объявляется как inline для оценки производительности.

Если шаблон не может решить эту проблему, будет очень много проблем, как показано ниже.

if (conditionA)
{
    typeA1    a;
    if (conditionB)
    {
        typeB1    b;
        if (conditonC)
        {
            C1    c;
            Function(a, b, c);
        }
        else
        {
            C2    c;
            Function(a, b, c);
        }
    }
    else
    {
        typeB2    b;
        if (conditonC)
        {
            C1    c;
            Function(a, b, c);
        }
        else
        {
            C2    c;
            Function(a, b, c);
        }
    }
}
else
{
    typeA2    a;
    if (conditionB)
    {
        typeB1    b;
        if (conditonC)
        {
            C1    c;
            Function(a, b, c);
        }
        else
        {
            C2    c;
            Function(a, b, c);
        }
    }
    else
    {
        typeB2    b;
        if (conditonC)
        {
            C1    c;
            Function(a, b, c);
        }
        else
        {
            C2    c;
            Function(a, b, c);
        }
    }
}

Ответы [ 6 ]

3 голосов
/ 09 января 2009

Результат условного оператора (то есть a и b в p ? a : b) должен быть одного типа. То есть вы не можете сделать:

predicate() ? 3.14 : "sdfsd"

Убедитесь, что ваши pa1 и pa2 являются совместимыми типами (либо они одного типа, либо наследуются от одного типа, либо приводятся к совместимым типам). Если у вас действительно есть функция-член convert, которая преобразует эти типы в совместимые типы, то почему бы просто не использовать:

conditionA ? pa1.convert() : pa2.convert()

Наконец, это не оооочень долго. Вы уже выписали определение. Просто держите его в общих чертах и ​​двигайтесь дальше.

2 голосов
/ 09 января 2009

Ваша основная проблема в том, что тип выражения должен быть понятен во время компиляции .

Является ли условие фиксированным и известным во время компиляции? Если это так, можно будет использовать метафункцию для выбора каждого параметра:

template <bool B>
struct choose {
    typedef X type;
};

template <>
struct choose<false> {
    typedef Y type;
};

...

func(choose<a_constant_bool_expr>::type());

(Упрощено для случая с 1 параметром; для 3 параметров вы можете определить, например, struct choose1, struct choose2, struct choose3.)

В противном случае, ваш лучший вариант - извлечь все типы из общей базы, как предложил Джон Цвинк. Единственной альтернативой этому является гигантский оператор switch или список if s.

На самом деле, может быть, это то, для чего Boost.Variant подойдет?

2 голосов
/ 09 января 2009

Сделайте pa1 и pa2 наследуемыми от общего базового класса и используйте ссылку на этого предка в качестве типа аргумента вашей (тогда не шаблонной) функции.

1 голос
/ 09 января 2009

Это немного уродливо, но вы можете превратить 3-уровневую вложенность в один оператор switch, используя биты:

const unsigned int caseA = 1 << 0;
const unsigned int caseB = 1 << 1;
const unsigned int caseC = 1 << 2;
switch ((conditionA ? caseA : 0) | (conditionB ? caseB : 0) | (conditionC ? caseC : 0)) {
    case 0:                            func(pa2, pb2, pc2); break;
    case caseA:                     func(pa1, pb2, pc2); break;
    case caseB:                     func(pa2, pb1, pc2); break;
    case caseA|caseB:           func(pa1, pb1, pc2); break;
    case caseC:                     func(pa2, pb2, pa1); break;
    case caseA|caseC:           func(pa1, pb2, pc1); break;
    case caseB|caseC:           func(pa2, pb1, pc1); break;
    case caseA|caseB|caseC: func(pa1, pb1, pc1); break;
    default: assert(false); // unreachable
}

Это разделило вашу серию из 3 бинарных решений в одно 8-стороннее решение, поэтому об этом проще думать. Некоторые люди могут ненавидеть это, но я нахожу это вполне читабельным.

1 голос
/ 09 января 2009

У вас проблема со строгой типизацией во время компиляции, и вы хотите изменить тип во время выполнения. Когда шаблон для функции скомпилирован, он должен знать, какие типы предусмотреть. Вы можете использовать разные типы, вы просто не можете сделать все это в одну строку.

Неужели делать convert () так дорого, что вы захотите делать это только в случае необходимости в Function?

Отойдя от шаблона ...

Ваше описание сделало длину метода хуже, чем может показаться, вам нужно, чтобы ваши a's b и c уже были определены. Вы также можете сократить условия до оператора switch.

Если вы сохранили a, b и c как законченные объекты и могли бы попросить его передать значение функции.

class a {
  p1, p2;
  condition;
  val value() { return condition? p1.convert, p2.convert };
}

Вы можете сделать так, чтобы они имели интерфейс, общий для тех методов, которые необходимы в функции. Есть несколько способов сделать это, но проще всего, если вы можете изменить класс для TypeA1 и т. Д. Добавление родительского класса чего-то вроде IConvertable здесь.

class IConvertable{
public: 
  ~IConvertable(){}
  virtual val convert() = 0;
}

И затем реализация преобразования в каждом классе для вызова статической версии преобразования.

0 голосов
/ 09 января 2009

Продолжая (повторяя) то, что говорили все остальные ...

Это не будет работать:

template<class TYPE>
inline void Function( TYPE & object )
{
  cout << "Function():  " << object.convert() << endl;
}

class A
{
public:
  static const char * convert() { return "classA"; }
};

class B
{
public:
  static const char * convert() { return "classB"; }
};

int
main(int argc)
{
  A a;
  B b;

  Function( argc>1 ? a : b );
}

Поскольку a и b являются различными типами, и шаблонная Функция создается для вас на основе типа аргумента.

Но это БУДЕТ работать:

template<class TYPE>
inline void Function( TYPE & object )
{
  cout << "Function():  " << object.convert() << endl;
}

class C
{
public:
  virtual const char * convert() = 0;
};

class A : public C
{
public:
  static const char * staticConvert() { return "classA"; }
  const char * convert() { return A::staticConvert(); }
};

class B : public C
{
public:
  static const char * staticConvert() { return "classB"; }
  const char * convert() { return B::staticConvert(); }
};

int
main(int argc)
{
  A a;
  B b;

  Function( argc>1 ? (C&)a : (C&)b );
}

Хотя я действительно должен использовать dynamic_cast ...

  Function( argc>1 ? dynamic_cast<C&>(a) : dynamic_cast<C&>(b) )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...