Вопрос

Как и большинство программистов, я восхищаюсь принципами Грамотного программирования и стараюсь следовать им, но в C ++ я обычно использую std::pair, для миллиона общих задач.Но std::pair является, ИМХО, злейшим врагом грамотного программирования...

Я хочу сказать, что когда я возвращаюсь к коду, который написал день или два назад, и я вижу манипуляции с std::pair (обычно в качестве итератора) Я удивляюсь сам себе "что означали iter-> первый и iter-> второй???".

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

Это было полезно?

Решение

Как насчет этого?

struct MyPair : public std::pair < int, std::string >
{
    const int& keyInt() { return first; }
    void keyInt( const int& keyInt ) { first = keyInt; }
    const std::string& valueString() { return second; }
    void valueString( const std::string& valueString ) { second = valueString; }
};

Это немного многословно, однако использование этого в вашем коде может немного облегчить чтение, например:

std::vector < MyPair > listPairs;

std::vector < MyPair >::iterator iterPair( listPairs.begin() );
if ( iterPair->keyInt() == 123 )
    iterPair->valueString( "hello" );

Кроме этого, я не вижу никакой серебряной пули, которая прояснит ситуацию.

Другие советы

std :: pair - хороший способ сделать " локальным " и по существу анонимный тип с по существу анонимными столбцами; если вы используете определенную пару на таком большом лексическом пространстве, что вам нужно назвать тип и столбцы, я бы вместо этого использовал простую struct .

typedef std::pair<bool, int> IsPresent_Value;
typedef std::pair<double, int> Price_Quantity;

... вы поняли.

Вы можете создать две пары получателей (const и non), которые будут просто возвращать ссылку на первое и второе, но будут гораздо более удобочитаемыми. Например:

string& GetField(pair& p) { return p.first; }
int& GetValue(pair& p) { return p.second; }

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

Если вы планируете много использовать это, вы также можете создать макрос, который будет генерировать эти геттеры для вас, учитывая имена и типы: MAKE_PAIR_GETTERS (Field, string, Value, int) или около того. Простое получение методов получения, возможно, позволит компилятору оптимизировать их, поэтому они не добавят накладных расходов во время выполнения; и использование макроса позволит легко создавать эти геттеры для любого использования пар.

Вы можете использовать буст-кортежи, но они на самом деле не меняют основную проблему: хотите ли действительно получить доступ к каждой части пары / кортежа с помощью небольшого целочисленного типа, или вы хотите более «грамотный» код. См. этот вопрос , который я недавно отправил.

Однако boost :: необязательно - это полезный инструмент, который, как я обнаружил, заменяет довольно много случаев, когда пары / кортежи рекламируются как ответ.

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

typedef boost::tuple<int, int> KeyValueTuple;
enum {
  KEY
  , VALUE
};

void foo (KeyValueTuple & p) {
    p.get<KEY> () = 0;
    p.get<VALUE> () = 0;
}

void bar (int key, int value)
{
  foo (boost:tie (key, value));
}

Кстати, комментарии приветствуются, если есть скрытая плата за использование этого подхода.

РЕДАКТИРОВАТЬ: удалить имена из глобальной области.

Просто быстрый комментарий относительно глобального пространства имен. В общем, я бы использовал:

struct KeyValueTraits
{
  typedef boost::tuple<int, int> Type;
  enum {
    KEY
    , VALUE
  };
};

void foo (KeyValueTuple::Type & p) {
    p.get<KeyValueTuple::KEY> () = 0;
    p.get<KeyValueTuple::VALUE> () = 0;
}

Похоже, что boost :: fusion связывает идентичность и значение ближе друг к другу.

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

Мне также не нравится std::pair, используемый в std:: map, записи карты должны были иметь ключ members и значение.
Я даже использовал boost::MIC, чтобы избежать этого.Однако boost::MIC также имеет свою стоимость.

Кроме того, возврат std::pair приводит к получению менее читаемого кода:

if (cntnr.insert(newEntry).second) { ... }

???

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top