2016-11-20 2 views
2

В настоящее время я использую std::map, чтобы сохранить пар ключ/значение:Сделать станд :: установлен использовать оператор преобразования при сравнении элементов

#include <map> 

using K = int; 
struct P {}; // some useful payload 

int main() 
{ 
    std::map< K, P const > m; 
    m.insert({1, {}}); 
    auto it = m.find(1); 
    // access: 
    it->first; 
    it->second; 
} 

int здесь только для примера. mapped_type всегда const в моем случае.

Для доступа к полезной нагрузке P const Мне нужно использовать не слишком информативное имя second. То же самое касается first. Я хочу назвать это просто payload или как-нибудь еще.

Чтобы достичь этого, я изобрел следующий подход:

#include <set> 

using K = int; 
struct P {}; 

struct A 
{ 
    K key; 
    P payload; 
    operator K const &() const { return key; } 
}; 

struct less 
{ 
    using is_transparent = void; 
    bool operator() (K const & l, K const & r) const 
    { 
     return l < r; 
    } 
}; 

int main() 
{ 
    std::set< A, less > s; 
    s.insert({1, {}}); 
    auto it = s.find(1); 
    // access: 
    it->key; 
    it->payload; 
} 

Здесь я делаю std::set использовать оператор преобразования каждый раз, когда для типа ключа. Оно работает. Но запрещен ли подход? Есть undefined поведение?

+0

Выглядит отлично. (Но я не уверен, что опубликую его как ответ) – krzaq

+0

Многие программисты на С ++ держатся подальше от использования операторов преобразования из-за вопросов, подобных вашим. Слишком много случаев, операторы преобразования либо вызывают проблемы компиляции, либо, если это не так, возникают проблемы времени выполнения, когда программист обнаруживает, что их операция преобразования используется, не зная, что она используется. Отладка таких программ превращается в болезненную ситуацию. – PaulMcKenzie

+0

@PaulMcKenzie Вы правы. Но я хочу использовать выше подход где-то в глубине моей библиотеки. Пользовательский код будет далек от таких подходов, подверженных ошибкам. – Orient

ответ

3

Для меня это выглядит технически обоснованным.

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

Действительно, тот факт, что вы — собственного автора кода, не меньше! — почувствовал необходимость приехать сюда, прося адвоката языка, чтобы проверить правильность кода - это большой красный флаг, который, вероятно, не стоит.

Если в любой момент вы чувствуете, что .first и .second являются недостаточно информативны для вашего кода, вы можете вместо этого обойти, что локально с такой магии:

auto& key  = it->first; 
auto& payload = it->second; 

или даже:

auto& payload = getPayload(it); 

где getPayload - это подходящая функция, использующая итератор вашего конкретного типа.

Эти подходы имеют преимущество быть достаточно очевидными и не требуют созыва сессии Верховного суда C++, чтобы сначала проверить их.

+0

Для C++ 1z он также может быть четко выражен в терминах декомпозиции (структурированные привязки). 'auto & [ключ, полезная нагрузка] = * it'. Но все же это дополнительный слой косвенности. – Orient

+0

@Orient Каким образом это дополнительный слой косвенности? Вы имеете в виду лексически, не так ли? –

+0

Лексически? Может быть, если вы уверены, что ссылки будут оптимизированы. – Orient

Смежные вопросы