Аргумент по умолчанию для вектора функций компаратора - PullRequest
0 голосов
/ 03 июня 2018

Интересующая функция

Я пытаюсь написать шаблонную функцию sortDifferentWays, которая будет сортировать (и печатать) вектор, следуя инструкциям функции компаратора.Я не уверен, каковы стандартные способы указания компараторов, но я пошел с

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators)
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

Пример реализации

Функция, например, может быть реализована в коде

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

class X
{
public:
    int a;
    int b;

    X (int A, int B)
    : a(A), b(B)
    {}

    std::string toString()
    {
        return "{"+std::to_string(a)+","+std::to_string(b)+"}";
    }
};

void printVec(std::vector<int> v)
{
    for (auto& elem : v)
        std::cout << elem <<" ";
    std::cout << "\n";
}

void printVec(std::vector<X> v)
{
    for (auto& elem : v)
        std::cout << elem.toString() <<" ";
    std::cout << "\n";
}

bool compare_a(X left, X right)
{
    return left.a < right.a;
}

bool compare_b(X left, X right)
{
    return left.b < right.b;
}

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators)
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

int main()
{
    std::vector<X> v = {{1,5},{4,4},{9,2},{4,4},{0,6},{12,2},{11,9}};
    sortDifferentWays(v, {&compare_a, &compare_b});

    return 0;
}

, который выдает

{0,6} {1,5} {4,4} {4,4} {9,2} {11,9} {12,2} 
{9,2} {12,2} {4,4} {4,4} {1,5} {0,6} {11,9}

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

Вопрос

Я бы хотел, чтобы sortDifferentWays имел значение по умолчанию comparatorsаргумент, содержащий одну функцию, которая будет хорошо работать с большинством (возможно, со всеми) примитивными типами.

template<typename T>
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators = {[](const T& left, const T& right) -> bool{return left < right;}})
{
    for (auto& comparator : comparators)
    {
        std::sort(v.begin(), v.end(), comparator);
        printVec(v);
    }
}

int main()
{
    std::vector<X> v = {{1,5},{4,4},{9,2},{4,4},{0,6},{12,2},{11,9}};
    sortDifferentWays(v, {&compare_a, &compare_b});

    std::vector<int> v2 = {4,5,8,3,9,10,2};
    sortDifferentWays(v2);

    return 0;
}

Но он не скомпилируется с сообщением об ошибке

test.cpp:47:70: error: no matching constructor for initialization of 'std::vector<bool (*)(int, int)>'
void sortDifferentWays(std::vector<T>& v, std::vector<bool (*)(T,T)> comparators = {[](const T& left, const T& right) -> bool{return left < right;}})

Я попытался определить defaultComparator как

template<typename T>
bool defaultComparator(const T& left, const T& right)
{
    return left < right;
}

и дать comparators = {&defaultComparator}, но он тоже не сработал и выдал сообщение об ошибке

error: reference to overloaded function could not be resolved; did you mean to call it?

Как мне сделать компаратор по умолчанию?

РЕДАКТИРОВАТЬ

Я компилирую с

g++ test.cpp -o a -std=c++11

, где g++ по умолчанию установлен на clang (MAC OSX)

g++ --version
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple LLVM version 9.1.0 (clang-902.0.39.1)
Target: x86_64-apple-darwin17.5.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

1 Ответ

0 голосов
/ 03 июня 2018

Я вижу пару проблем в этом примере, однако та, которую вы ищете, связана с аргументом comparators.

Это было определено как std::vector<bool (*)(T,T)>, который является векторомфункциональные указатели.Однако вы пытаетесь инициализировать его с помощью {[](const T& left, const T& right) -> bool{return left < right;}}.Это список инициализаторов lambdas, который нельзя преобразовать.

Причина этого в том, что лямбды - это быстрый способ написания класса с перегруженной operator().

class lambda
{
public:
    template<typename T1, typename T2>
    auto operator()(const T1 &left, const T2 &right) const -> bool { return left < right; }
};

Очевидно,этот класс не может быть преобразован в указатель на функцию.Чтобы обрабатывать как указатели на функции, так и классы (или функторы), лучше использовать тип std::function из <functional>.В вашем случае std::function<bool(T,T)>.

Редактировать Monkey0506 указал, что лямбды могут быть преобразованы в указатели функций в случае, если нет захвата и они имеют одинаковую подпись.Последнее не соответствует действительности, поскольку аргументы лямбды являются константными ссылками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...