Может быть способ добиться того, чего вы хотите, с помощью шаблонов выражений. Ниже приведен набросок того, как к этому подойти (не компилируется, отсутствует много деталей, предупредите лектора). Сначала вы настраиваете шаблон класса для представления логических значений и определяете над ними некоторые операторы.
template<typename T, typename Type = Atomic<T> >
class Logical;
template<typename T, typename E1, typename E2>
Logical<T, OpOr<T, E1, E2> > operator||(Logical<T, E1> lhs, Logical<T, E2> rhs);
template<typename T, typename E1, typename E2>
Logical<T, OpAnd<T, E1, E2> > operator&&(Logical<T, E1> lhs, Logical<T, E2> rhs);
template<typename T, typename E1, typename E2>
Logical<T, OpEq<T, E1, E2> > operator==(Logical<T, E1> lhs, Logical<T, E2> rhs)
{ return OpEq<T, E1, E2>()(lhs, rhs); } // delegate to class template
Поскольку шаблоны функций не могут быть частично специализированными, вы делегируете фактическую работу шаблонам классов.
// primary template
template<typename T, typename E1, typename E2> class OpEq;
// specialization for atomic comparisons
template<typename T>
class OpEq<T, Atomic<T>, Atomic<T> >
{
bool operator()(Atomic<T> lhs, Atomic<T> rhs)
{ return lhs == rhs; }
}
// apply distributive rule
template<typename T>
class OpEq<T, Atomic<T>, OpOr<T, Atomic<T>, Atomic<T> > >
{
bool operator()(Atomic<T> lhs, OpOr<T, Atomic<T>, Atomic<T> > rhs)
{ return (lhs == rhs.first()) && (lhs == rhs.second()); }
}
Очевидно, что для получения естественного синтаксиса C++ для того, что вы хотите, требуется много тяжелой техники шаблонов. Но, приложив много усилий и прочитав, вы можете в конечном итоге получить что-то хорошее. (Вам нужно будет определить Atomic, OpAnd, OpOr, настроить представления, содержащие первую и вторую ветви подвыражения и т.д. и т.д.)
Однако, даже если бы вам это удалось, вы бы получили действительно странную семантику в своей схеме. То, что вы предлагаете, требует, чтобы ==
было левораспределительным по сравнению с ||
или &&
. т.е. разбирать
X == (Y @OP Z)
as
(X == Y) @OP (X == Z)
где @OP
равно &&
или ||
. Я думаю, было бы естественно потребовать, чтобы ==
оставался симметричным. Это потребует от вас также наложить право-распределение ==
на &&
и ||
. т.е. разбирать
(X @OP Y) == Z
as
(X == Z) @OP (Y == Z)
Однако, если вы объедините их с выражением (A @OP1 B) == (C @OP2 D)
, вы получите логические несоответствия. Например. результат зависит от порядка, в котором вы применяете левое и правое распределение.
Слева-направо:
(A @OP1 B) == (C @OP2 D)
((A @OP1 B) == C) @OP2 ((A @OP1 B) == D)
((A == C) @OP1 (B ==C)) @OP2 ((A == D) @OP1 (B == D))
Направо-налево:
(A @OP1 B) == (C @OP2 D)
(A == (C @OP2 D)) @OP1 (B == (C @OP2 D))
((A == C) @OP2 (A == D)) @OP1 ((B == C) @OP2 (B == D))
В обоих случаях сравниваются одни и те же 4 пары элементов, но способ их распространения вверх по дереву выражений немного отличается. Если @OP1
и @OP2
совпадают, вы можете сгладить все дерево и изменить порядок членов, чтобы получить уникальный результат. , это работает нормально, если вы используете одни и те же операторы с обеих сторон ==
, потому что и &&
, и ||
являются ассоциативными, а также коммутативными.
Но для смешанных операторов результирующие выражения, вообще говоря, будут другими.
ОБНОВЛЕНИЕ: как упоминалось в комментариях к этому и другим ответам, вы также теряете определенные свойства встроенных типов. Во-первых, правила короткого замыкания, которым не следуют перегруженные операторы. Для логических выражений, не связанных с разыменованием указателя или другим доступом к ресурсам (if(p && p->value())
или if(file && file.open())
и т. д.), это не повлияет на правильность, а только на эффективность. В противном случае будьте осторожны! Во-вторых, было также упомянуто, что смешанные оценки констант/выражений будут ошибочными. У этого есть простое (но многословное) исправление: просто используйте std::integral_constant
(или boost::mpl::int_
) в качестве оболочки.
person
TemplateRex
schedule
20.07.2012
if (areAllEqual (X, Y, Z, A, B, C))
- person chris   schedule 20.07.2012