Подтвердить что ты не робот

Есть ли удобный способ привязать std:: pair как новый тип?

Часто мне приходится использовать std:: pair для определения логических группировок двух связанных величин как аргументов функции/возвращаемых значений. Некоторые примеры: строка/столбец, тег/значение и т.д.

Часто мне нужно катавать собственный класс, а не просто использовать std:: pair. Это довольно легко увидеть, когда все начинает разрушаться - когда код становится заваленным make_pair, во-первых, и во-вторых, его очень сложно запомнить, что есть что - std::pair<int, int> передает меньше значения, чем тип Position.

Что вы нашли, это лучшие способы обернуть функциональность std:: pair в тип, который передает реальный смысл?

Вот некоторые вещи, которые я рассмотрел:

typedef std::pair<int, int> Position;

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

struct Position : public std::pair<int, int>
{
    typedef std::pair<int, int> Base;
    Position() : Base() {}
    Position(const Position &x) : Base(x) {}
    Position(int a, int b) : Base(a, b) {}

    int &row() { return first; }
    const int &row() const { return first; }

    int &col() { return second; }
    const int &col() const { return second; }
};

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

Очевидным следующим шагом является наследование private:

struct Position : private std::pair<int, int>
{
    typedef std::pair<int, int> Base;
    Position() {}
    Position(const Position &x) : Base(x) {}
    Position(int a, int b) : Base(a, b) {}

    int &row() { return first; }
    const int &row() const { return first; }

    int &col() { return second; }
    const int &col() const { return second; }

    bool operator<(const Position &x) const { return Base(*this) < Base(x); }
    // other forwarding operators as needed...
};

Итак, теперь, по крайней мере, мы избавились от доступа к первому и второму, но теперь появляется новая проблема. Когда мы хотим сохранить тип в std:: set, теперь у нас нет доступа к оператору < перегрузка, поскольку у нас нет доступа к первой и второй. Это означает, что мы должны определить функцию пересылки для каждой требуемой перегрузки оператора. Для меня это обычно ==,!= И <, но могут быть другие, которые я бы хотел. Да, я знаю, что я, вероятно, не должен перегружать оператора < просто придерживаться его в ассоциативном контейнере, но он делает все настолько простыми... И определение этих операторов для каждого нового типа является болью, и мы STILL должны получить доступ через функции. Мы можем исправить это:

struct Position
{
    Position() {}
    Position(const Position &x) : row(x.row), col(x.col) {}
    Position(int row, int col) : row(row), col(col) {}

    int row, col;
};
bool operator<(const Position &a, const Position &b)
{
    return a.row < b.row || (!(b.row < a.row) && a.col < b.col);
}
// more overloads as needed

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

Есть ли какие-либо решения, которые я упускал из виду, сделать это легко без недостатков? Если вы не хотите, чтобы вы предпочитали?

4b9b3361

Ответ 1

Это то, что было сделано Boost.Tuple.

Но вы, вероятно, должны теперь использовать std:: tuple...

Ответ 2

Сотрудник указал мне на два возможных решения:

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

Использование макроса для генерации кода, необходимого для разных операторов. Таким образом, мне не нужно было бы явно писать что-либо на уровне определения, просто сделайте что-то вроде DEFINE_PAIR_TYPE(Position, int, int, row, col);. Это, вероятно, ближе всего к тому, что я ищу, но он по-прежнему чувствует себя злым по сравнению с некоторыми решениями, представленными другими.

Ответ 3

Кроме того, библиотека Boost:: Operators автоматически генерирует код оператора. Это похоже на библиотеку SGI, которую предложил Martin York, но может быть более портативным.

Ответ 4

Вы можете повторно использовать функциональность pair, пересылая ей:

bool operator< ( const Position &a, const Position &b ) 
{
    return
        std::make_pair( a.row, a.col ) < std::make_pair( b.row, b.col );
}

Хотя вы все еще делаете это для каждой операции, вам нужно...

Ответ 5

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

#include <utility>

http://www.sgi.com/tech/stl/operators.html

Требования к типам

Требование оператора!= состоит в том, что x == y является допустимым выражением Требование операторa > состоит в том, что y < x - допустимое выражение Требование оператора <= состоит в том, что y < x - допустимое выражение Требование операторa >= состоит в том, что x < y является допустимым выражением

Таким образом, в основном он автоматически генерирует другие операторы, и == все, что вам нужно сделать, это включить <utility>

Ответ 6

Я должен сказать, что много думал просто создать простую структуру.

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

struct MyStruct
{
    std::string var1;
    std::string var2;
    bool var3;

    struct less : std::binary_function<struct MyStruct, struct MyStruct, bool>
    {
        bool operator() (const struct MyStruct& s1, const struct MyStruct& s2) const
            { if (var1== a2.var1) return var2 < a2.var2; else return var3 < a2.var3; }
    };
};
typedef std::set<struct MyStruct, MyStruct::less> MySet;

или поместите их внутри определения класса

bool operator==(const MyStruct& rhs) const 
    { return var1 == rhs.var1 && var2 == rhs.var2 && var3 == rhs.var3; };
bool operator<(const MyStruct& a2) const  
    { if (var1== a2.var1) return var2 < a2.var2; else return var3 < a2.var3; };

Лучшие причины в том, что его легко понять выше, они легко могут быть легко удалены в определение класса, и их легко расширить, если вы обнаружите, что вам нужно больше переменных позже. Я бы никогда не попытался перегрузить std:: pair, когда было гораздо более простое решение.

Ответ 7

Не используйте его.

Я ненавижу std:: pair именно по этой причине. Вы никогда не знаете, что есть, и поскольку доступ к первому и второму является общедоступным, вы также не можете выполнять контракты.

Но ведь это вопрос вкуса.

Ответ 8

К сожалению strong typedef s не попадет в С++ 0x, ему была дана классификация Неготова для С++ 0x, но открыта для повторной отправки в будущем.