2016-10-01 3 views
1

Я хочу установить ключи std::map, используя элементы std::set (или std::vector).Как инициализировать карту из набора

-то вроде следующего ...

std::set<int> keys = { 3,4,6 }; 
std::map<int,string> results(keys); // syntax error 

Может ли это быть сделано без явного перебора множества?

+1

1) Есть ли какой-либо конкретной причине вы хотите? 2) Каковы значения строк значений? – Beta

+0

Я был бы очень удивлен, если бы это было возможно, поскольку доступ к неинициализированным значениям, которые могут возникнуть в результате этого утверждения, не должен быть допустимым поведением. – ApproachingDarknessFish

+0

@Beta: 1) Я хочу создать API, который позволяет ключу указываться кратко (без учета значений). 2) Я хочу, чтобы значение было инициализировано по умолчанию. – nobar

ответ

2

Вы не можете. A map не является set. Это принципиально разные контейнеры, даже если базовая структура похожа.


Это, все возможно. Конструктор диапазона std::map является линейным временем, если элементы диапазона уже отсортированы, что гарантирует нам set. Поэтому все, что вам нужно сделать, это применить трансформатор к каждому элементу для создания нового диапазона. Самым простым было бы просто использовать что-то вроде boost::make_transform_iterator (или свернуть свой собственный):

template <class K, class F 
    class V = decltype(std::declval<F&>()(std::declval<K const&>()))::second_type> 
std::map<K, V> as_map(std::set<K> const& s, F&& f) { 
    return std::map<K,V>(
     boost::make_transform_iterator(s.begin(), f), 
     boost::make_transform_iterator(s.end(), f)); 
} 

std::map<int,string> results = 
    as_map(keys, [](int i){ 
     return std::make_pair(i, std::string{}); 
    }); 

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

template <class V, class K> 
std::map<K, V> as_map_default(std::set<K> const& s) { 
    auto f = [](K const& k) { return std::make_pair(k, V{}); } 
    return std::map<K,V>(
     boost::make_transform_iterator(s.begin(), f), 
     boost::make_transform_iterator(s.end(), f)); 
} 

std::map<int,string> results = as_map_default<string>(keys); 
+0

Каждый раз, когда кто-то говорит «простейшим решением будет X», а X включает в себя строку, которая определяет «класс V», кто-то клянется, чтобы никогда не учиться C++. :-) Примечание: Мне нравится ответ, но человек, так много угловых скобок. – ShadowRanger

+0

Ответ начинается словами «вы не можете», а затем продолжает демонстрировать, как это сделать. Итерация скрыта внутри конструктора карты, и это в основном то, что я надеялся увидеть.Итак, это правильный ответ, за исключением первых двух слов. :) – nobar

1

Может ли это быть без явного перебора по множеству?

№ Нет. Невозможно узнать ключи в наборе без итерации по нему. Вы можете писать функции, чтобы сделать это , как если бы было неявное преобразование, но эти функции должны в конечном счете повторять исходную коллекцию.

Простой способ заключается в следующем:

#include <set> 
#include <map> 
#include <string> 

auto build_map(const std::set<int>& source) -> std::map<int,std::string> 
{ 
    std::map<int,std::string> results; 
    for (auto const& i : source) { 
    results[i]; 
    } 
    return results; 
} 

int main() 
{ 
    std::set<int> keys = { 3,4,6 }; 
    auto results = build_map(keys); 
} 

Конечно, мы можем templatise, если это улучшает читаемость:

#include <set> 
#include <vector> 
#include <unordered_set> 
#include <map> 
#include <string> 
#include <utility> 

template<class MappedType, class SourceContainer> 
auto build_map(SourceContainer&& source) 
{ 
    using source_type = std::decay_t<SourceContainer>; 
    using key_type = typename source_type::value_type; 

    std::map<key_type , MappedType> results; 
    for (auto const& i : source) { 
    results[i]; 
    } 
    return results; 
} 

int main() 
{ 
    std::set<int> keys = { 3,4,6 }; 
    auto results = build_map<std::string>(keys); 

    // also 
    results = build_map<std::string>(std::vector<int>{3, 4, 6}); 
    results = build_map<std::string>(std::unordered_set<int>{3, 4, 6}); 
} 
+0

Хороший пример того, как сделать инициализацию на основе итераций очень просто. – nobar

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