Как перегрузить метод, принимая предикат против значения в C ++ - PullRequest
1 голос
/ 08 февраля 2012

Пожалуйста, не стесняйтесь перефразировать заголовок, если он не подходит. Я пытаюсь придумать обертку поверх алгоритмов find и find_if в STL. Это то, что у меня есть сейчас.

template<typename Type, size_t SIZE>
int IndexOf(const Type(&arr)[SIZE], const Type& val)
{
    return IndexOf( arr, arr + SIZE, val);
}

template<typename Iter, typename Type>
int IndexOf(Iter first, Iter last, const Type& val)
{
    auto index = find(first, last, val);

    if ( index != last)
    {
        return distance(first, index);
    }
    return -1;
}


template<typename Type, size_t SIZE, typename Pred>
int IndexOf(const Type(&arr)[SIZE], Pred pred)
{
    return IndexOf(arr, arr + SIZE, pred);
}


template<typename Iter, typename Pred>
int IndexOf(Iter first, Iter last, Pred pred)
{
    auto index = find_if(first, last, pred);

    if ( index != last)
    {
        return distance(first, index);
    }
    return -1;
}

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

vector<string> names;
names.push_back("Jagan");
names.push_back("Gagan");
names.push_back("Magan");
names.push_back("Pagan");
names.push_back("Vagan");

std::cout << "Index of (Gagan)" << IndexOf(begin(names), end(names), 
                                          [](const string& name)
                                          {
                                        return name == "Gagan";
                                          }); 

Приведенный выше пример использования только для краткости.

Ответы [ 2 ]

3 голосов
/ 08 февраля 2012

Делайте так, как делает stdlib: Не перегружайте.Например, stdlib предоставляет две функции, которые позволяют вам искать что-то в диапазоне.

Один именуется find (например, «найдите это значение!»), А другой - find_if (например, «найдено, если предикат возвращает истину!»).


Другой вариант может заключаться в использовании некоторого трюка SFINAE (с использованием C ++ 11 и выражения SFINAE):

template<class T>
T create();

template<class InIt, class T>
auto find(InIt first, InIt last, T const& value)
    -> decltype((*first == value), create<InIt>())
{
  // ...
}

Эта функция будет удалена из набора перегрузки, если *first == value не является допустимым выражением(обычно вы не сохраняете предикаты в контейнере и выполняете поиск с тем же предикатом в нем).

, create<InIt>() в decltype должен придать ему тип возврата InIt.create не определено, потому что оно будет когда-либо использоваться только в неоцененных контекстах, и как таковое не нуждается в определении (которое облегчает жизнь, нам не нужно догадываться, как мы могли бы на самом деле построить такой объект. Мы просто беремтип InIt).Если бы я использовал first вместо create<InIt>(), тип возвращаемого значения был бы InIt&, что было бы немного удивительно и нехорошо, поскольку вы возвращаете ссылку на локальную переменную

template<class Init, class Pred>
auto find(InIt first, InIt last, Pred pred)
    -> decltype((pred(*first)?0:0), create<InIt>())
{
  // ...
}

Эта функция будет удалена из набора перегрузки, если pred(*first) не является допустимым выражением, т.е. pred не принимает value_type из InIt в качестве параметра.Кроме того, он будет удален, если тип возвращаемого значения pred operator() не может быть явно преобразован в bool, протестировано с использованием ?:, троичного логического оператора.Опять же, , create<InIt>() используется для того, чтобы придать функции возвращаемый тип InIt.

Вот небольшой живой пример на Ideone для вашего конкретного случая.Обратите внимание, что я не использовал create<int>() здесь, потому что литерал 0 уже имеет тип int.

0 голосов
/ 08 февраля 2012

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

#include <algorithm>
#include <iterator>
#include <type_traits>

template<typename Iter>
int IndexOf(Iter first, Iter last, typename std::iterator_traits<Iter>::value_type const & val)
{
    auto index = std::find(first, last, val);
    return index != last ? std::distance(first, index) : -1;
}

template<typename Iter, typename Pred>
typename std::enable_if<!std::is_constructible<typename std::iterator_traits<Iter>::value_type, Pred>::value, int>::type
IndexOf(Iter first, Iter last, Pred pred)
{
    auto index = std::find_if(first, last, pred);
    return index != last ? distance(first, index) : -1;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...