2014-10-08 5 views
0

Я хочу, чтобы оператор способен закорачивание оценки на trueилиfalse, но и имеющий доход, который указует на продолжение тестирования.оператора с троичным Возвращением и коротким замыканием


Например, лексикографическое сравнение строк между двумя строками first и second:

  • если first[0] < second[0], мы можем положить конец сравнения, возвращая true
  • в противном случае, если first[0] > second[0], мы можем закончить сравнение возвращая false
  • в противном случае, first[0] == second[0] имеет место, и нам нужно продолжить второй символ обеих строк.

тривиальное решение требует два сравнения:

bool firstIsEarlier(std::string first, std::string second){ 
    if(first[0] < second[0]){ 
     return true; 
    }else if(first[0] > second[0]){ 
     return false; 
    } 
    // first[0] == second[0] holds 
    // continue with second character.. 
} 

Мой хак решение было использовать if - else if блоки и int, которые дадут мне положительное число true или отрицательное число для false, a 0 указывает на продолжение тестирования.

bool firstIsEarlier(std::string first, std::string second){ 
    if(int i = first[0] - second[0]){ 
     return i < 0; 
    } 
    else if(i = first[1] - second[1]){ 
     return i < 0; 
    } 
    else if(i = first[2] - second[2]){ 
     return i < 0; 
    } 
    return false; 
} 

Так как вы можете видеть, что единственный способ для меня, чтобы заставить короткое замыкание перечислить каждое условие в качестве else if. Хорошим решением для меня было бы сделать это на одной линии и сохранить короткое замыкание. Отличное решение было бы, если бы была operator# сделать что-то вроде этого:

bool firstIsEarlier(std::string first, std::string second){ 
    return first[0] # second[0] ## first[1] # second[1] ## first[2] < second[2]; 
} 
+1

Я не понимаю, как вы сообщаете о необходимости продолжения тестирования в приведенном выше примере? –

+0

'true',' false' и '0'? Вопрос неясен! – Ajay

+0

@MarcoA. если 'first [0]' - тот же символ, что и 'second [0]' Мне нужно пройти тест следующего символа. В блоках 'if'-'sese if' я использую тот факт, что если символы совпадают, результат их вычитания будет равен 0. –

ответ

2

Вы вероятно должны добиться этого с

bool firstIsEarlier(std::string first, std::string second) { 
    return first < second; 
} 

или в более общем плане с std::lexicographical_compare.

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

Есть несколько ограничений, хотя:

  1. Вы не можете создавать новые операторы, так что вы должны выбрать два операторов, которые вы хотите перегружать: один для сравнения листьев и один для короткое замыкание.

    (Вы можете использовать один оператор, если вы действительно хотели, но это было бы (даже больше) сбивает с толку и требует много скобок)

  2. вы не можете сделать это, когда оба операнда являются примитивы , Если вы можете, ваш код будет выглядеть следующим образом:

    bool firstIsEarlier(std::string first, std::string second){ 
        return first[0]^second[0] <<= first[1]^second[1] <<= first[2]^second[2]; 
    } 
    

    , но на самом деле вы должны были бы обернуть char с в некотором значении контейнера для его работы.


Во-первых, нам нужно простое с тремя состояниями типа. Мы можем просто перечислить его:

enum class TriState { 
    True = -1, 
    Maybe = 0, 
    False = 1 
}; 

Далее, нам необходимо некоторое вещь представлять наше выражение first[0]^second[0] листа, которое приводится к нашему типу с тремя состояниями:

template <typename LHS, typename RHS> 
struct TriStateExpr { 
    LHS const &lhs_; 
    RHS const &rhs_; 

    TriStateExpr(LHS const &lhs, RHS const &rhs) : lhs_(lhs), rhs_(rhs) {} 

    operator bool() const { return lhs_ < rhs_; }  
    operator TriState() const { 
     return (lhs_ < rhs_ ? TriState::True : 
       (rhs_ < lhs_ ? TriState::False : TriState::Maybe) 
       ); 
    } 
}; 

Обратите внимание, что мы просто требуя рабочего operator< для наших типов - мы могли бы обобщить это, чтобы использовать явный компаратор, если это необходимо.

Теперь нам нужна нелистная часть дерева выражений. Я принуждаю его к дереву выражений справа налево, поэтому левое выражение всегда является листом, а правое выражение может быть листом или полным поддеревом.

template <typename LLHS, typename LRHS, typename RHS> 
struct TriStateShortCircuitExpr { 
    TriStateExpr<LLHS, LRHS> const &lhs_; 
    RHS const &rhs_; 

    TriStateShortCircuitExpr(TriStateExpr<LLHS, LRHS> const &lhs, RHS const &rhs) 
     : lhs_(lhs), rhs_(rhs) 
    {} 

    operator TriState() const { 
     TriState ts(lhs_); 
     switch (ts) { 
     case TriState::True: 
     case TriState::False: 
      return ts; 
     case TriState::Maybe: 
      return TriState(rhs_); 
     } 
    } 

    operator bool() const { 
     switch (TriState(lhs_)) { 
     case TriState::True: 
      return true; 
     case TriState::False: 
      return false; 
     case TriState::Maybe: 
      return bool(rhs_); 
     } 
    } 
}; 

Теперь вам нужен синтаксический сахар, поэтому нам нужно выбрать, какие операторы перегружать. Я буду использовать ^ для листьев (на том основании, что это как < поворачивается на 90 градусов по часовой стрелке):

template <typename LHS, typename RHS> 
TriStateExpr<LHS, RHS> operator^ (LHS const &l, RHS const &r) { 
    return TriStateExpr<LHS, RHS>(l,r); 
} 

и <<= для не листиков:

template <typename LLHS, typename LRHS, typename RLHS, typename RRHS> 
TriStateShortCircuitExpr<LLHS, LRHS, TriStateExpr<RLHS, RRHS>> 
    operator<<= (TriStateExpr<LLHS, LRHS> const &l, 
       TriStateExpr<RLHS, RRHS> const &r) { 
    return TriStateShortCircuitExpr<LLHS, LRHS, TriStateExpr<RLHS, RRHS>>(l, r); 
} 

template <typename LLHS, typename LRHS, typename... RARGS> 
TriStateShortCircuitExpr<LLHS, LRHS, TriStateShortCircuitExpr<RARGS...>> 
    operator<<= (TriStateExpr<LLHS, LRHS> const &l, 
       TriStateShortCircuitExpr<RARGS...> const &r) { 
    return TriStateShortCircuitExpr<LLHS, LRHS, 
            TriStateShortCircuitExpr<RARGS...>>(l, r); 
} 

Основные соображения о том, что оператор листа должен в идеале иметь более высокий приоритет, а оператор, не связанный с листом, должен связывать права налево. Если вместо этого вы использовали ассоциативный оператор слева направо, TriStateShortCircuitExpr::operator рекурсивно выполнил бы левый поддерево, что кажется неэлегантным для этого приложения.

+0

WOW! Я действительно хочу извлечь выгоду из понимания вашей тяжелой работы здесь. Я пытаюсь обвести голову вокруг этого. Правильно ли я понимаю, что даже если первый аргумент «TRUE» или «FALSE», должен быть сконструирован «TriStateShortCircuitExpr»? Например, это должно создать 2 'TriStateShortCircuitExpr', но оценивать только первый 'lhs_' при нажатии на' bool': FAL^SE << = MAY^BE << = MAY^BE –

+0

Yup, построено все дерево выражений , и только оценка короткозамкнута. Надеемся, что оценка будет либо дорогостоящей по сравнению со строительством, либо связана с некоторым побочным эффектом, который мы теперь можем избежать. – Useless

 Смежные вопросы

  • Нет связанных вопросов^_^