2008-11-05 3 views
14

У меня есть некоторые функции, чтобы найти значение:Как я могу скрыть функтор в C++ (STL)?

struct FindPredicate 
{ 

    FindPredicate(const SomeType& t) : _t(t) { 
    } 
    bool operator()(SomeType& t) { 
     return t == _t; 
    } 

private: 
    const SomeType& _t; 
}; 

bool ContainsValue(std::vector<SomeType>& v, SomeType& valueToFind) { 
    return find_if(v.begin(), v.end(), FindPredicate(valueToFind)) != v.end(); 
} 

Теперь я хотел бы написать функцию, которая проверяет, если все члены вектора удовлетворяют этот предикат:

bool AllSatisfy(std::vector<SomeType>& v) { 
    /* ... */ 
} 

Одно из решений заключается в использовании алгоритм std::count_if.

Кто-нибудь знает решение, которое предполагает отрицание предиката?

ответ

20

Лучшим решением является использование STL functional library. Получив предикат от unary_function<SomeType, bool>, вы сможете использовать функцию not1, которая делает именно то, что вам нужно (т. Е. Отрицает унарный предикат).

Вот как вы могли бы сделать это:

struct FindPredicate : public unary_function<SomeType, bool> 
{ 
    FindPredicate(const SomeType& t) : _t(t) {} 

    bool operator()(const SomeType& t) const { 
     return t == _t; 
    } 

private: 
    const SomeType& _t; 
}; 

bool AllSatisfy(std::vector<SomeType>& v, SomeType& valueToFind) 
{ 
    return find_if(v.begin(), 
        v.end(), 
        not1(FindPredicate(valueToFind))) == v.end(); 
} 

Если вы хотите, чтобы катить собственное решение (которое, IMHO, не самый лучший вариант ...), ну, вы могли бы написать другой предикат, который является отрицанием первого:

struct NotFindPredicate 
{ 

    NotFindPredicate(const SomeType& t) : _t(t) { 
    } 
    bool operator()(SomeType& t) { 
     return t != _t; 
    } 

private: 
    const SomeType& _t; 
}; 

bool AllSatisfy(std::vector<SomeType>& v) { 
    return find_if(v.begin(), 
        v.end(), 
        NotFindPredicate(valueToFind)) == v.end(); 
} 

Или вы могли бы сделать лучше и написать шаблон функтор инвертор, как:

template <class Functor> 
struct Not 
{ 
    Not(Functor & f) : func(f) {} 

    template <typename ArgType> 
    bool operator()(ArgType & arg) { return ! func(arg); } 

    private: 
    Functor & func; 
}; 

, что вы могли бы использовать следующим образом:

bool AllSatisfy(std::vector<SomeType>& v, SomeType& valueToFind) 
{ 
    FindPredicate f(valueToFind); 
    return find_if(v.begin(), v.end(), Not<FindPredicate>(f)) == v.end(); 
} 

Конечно, последнее решение лучше, потому что вы можете повторно использовать Не-структуру с каждым функтора вы хотите.

+0

И тогда вы можете добавить шаблон функции, как прокладка в SGI люди сделали, чтобы вернуть Не объект без необходимости указывать его тип. – xtofl 2008-11-05 19:55:17

7

См. Библиотечный функтор std not1, он возвращает функтор, который не является логическим, а не тем, что возвращает его функтор.

Вы должны быть в состоянии сделать что-то вроде:

bool AllSatisfy(std::vector<SomeType>& v, SomeType& valueToFind) { 
    return find_if(v.begin(), v.end(), not1(FindPredicate(valueToFind))) != v.end(); 
} 
2

В первый раз я использовал not1 я задавался вопросом, почему это не было просто называется not.

Ответ меня немного удивил (см. Комментарий).

+0

Очевидно, что `not 'является зарезервированным альтернативным представлением символа`! `(Раздел 2.11.2 в стандарте [lex.key]) – Motti 2008-11-05 15:20:11

0

Поскольку вы используете его, вам не нужен функтор FindPredicate, так как в примере вы проверяете только равенство.

bool all_equal(std::vector<SomeType>& v, SomeType& valueToFind) 
{ 
    return v.end() == find_if(v.begin(), v.end(), std::bind1st (equal_to(), valueToFind)); 
} 

bool all_not_equal(std::vector<SomeType>& v, SomeType &valueToFind) { 
{ 
    return v.end() == find_if(v.begin(), v.end(), std::bind1st (not_equal_to(), valueToFind)); 
} 

И вы можете просто создать этот шаблон самостоятельно.

template< typename InputIterator , typename Predicate > 
bool test_all(InputIterator first, InputIterator last, Predicate pred) 
{ 
    return last == find_if(first, last, pred); 
} 

test_all(v.begin(), v.end(), std::bind1st(not_equals_to_(value)));