1

Тема: map з tuple в якості набору значень

Хочу створити щось на зразок словника з кольорами.
Створюється й заповнюється нормально:

map<string, tuple<string, string, string, string>> dictcolors;
dictcolors.emplace(piecewise_construct,
                       forward_as_tuple("Freedom"),
                       forward_as_tuple("gold", "darkblue", "blue", "yellow"));

Але коли я хочу отримати доступ до значень ось так:

dictcolors["Freedom"]

Чи ось так:

for (int i=0; i<4; i++)
        cout << get<i>(dictcolors["Freedom"]) << endl;

Видає помилку.
Хоча якщо зробити ось так:

cout << get<0>(dictcolors["Freedom"]) << endl;

я отримую значення відповідного елемента в tuple.

Не розумію, чому не можу отримати значення, вказавши ключ.
Чи чому номери елементів при звернення через get<>  неможливо ітерувати через for.

2 Востаннє редагувалося koala (24.08.2023 14:48:02)

Re: map з tuple в якості набору значень

0. Коли отримуєте помилку - читайте її. Не виходить - копіюйте сюди. Проста констатація "отримую помилку" може означати, що ця помилка - "закінчилося місце на диску", але як ми можемо це відгадати?
1. dictcolors["Freedom"] у мене працює. І у вас теж (ви ж наводите приклад, коли це працює). Ви його просто неправильно використовуєте, але ж ви не наводите код, в якому він "не працює", тому і підказати вам, що виправити, ми не можемо.
2.

for (int i=0; i<4; i++)
        cout << get<i>(dictcolors["Freedom"]) << endl;

точно не спрацює. get<i> - це шаблон, він розкривається під час компіляції. А значення i обчислюються під час виконання, тобто вже після завершення компіляції. Якщо вам треба в циклі обходити елементи колекції, то вам потрібен масив, вектор, список, щось іще, але точно не тьюпл.

Подякували: leofun01, Teg Miles2

3

Re: map з tuple в якості набору значень

Ось так не працює:

cout << dictcolors["Freedom"] << endl;

error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'std::map<std::basic_string<char>, std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>>::mapped_type' (aka 'std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>'))

4

Re: map з tuple в якості набору значень

Vitaliy_Danmer написав:
map<string, tuple<string, string, string, string>> dictcolors;
dictcolors.emplace(piecewise_construct,
                       forward_as_tuple("Freedom"),
                       forward_as_tuple("gold", "darkblue", "blue", "yellow"));

Чому forward_as_tuple("Freedom") ? , ти ж не дав tuple<string>.
Замість tuple<string, string, string, string> дай vector<string>, ти ж хочеш застосовувати цикл, значить ти мусиш гарантувати що всі елементи є одного типу.

Vitaliy_Danmer написав:

Видає помилку.

Яку ? Код і текст помилки сюди.

Vitaliy_Danmer написав:

Не розумію, чому не можу отримати значення, вказавши ключ.

Для словника (map) можна.

5

Re: map з tuple в якості набору значень

Vitaliy_Danmer написав:

Ось так не працює:

cout << dictcolors["Freedom"] << endl;

error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'std::map<std::basic_string<char>, std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>>::mapped_type' (aka 'std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>'))

Я підкреслив ключові місця. Перекладаю: компілятор не знає, як саме вивести за допомогою оператора << ваш тьюпл у ostream (це тип cout). В C++ не визначена стандартна операція виведення для тьюплів, бо невідомо, як саме вам треба їх виводити. Ви можете (якщо вам це дуже треба) визначити власний operator << для тьюпла, але якщо ви використовуєте конкретний тьюпл так часто, що вам потрібні для нього перевантажені оператори, то варто вже його класом зробити.

Подякували: leofun01, Teg Miles2

6

Re: map з tuple в якості набору значень

leofun01 написав:
Vitaliy_Danmer написав:
map<string, tuple<string, string, string, string>> dictcolors;
dictcolors.emplace(piecewise_construct,
                       forward_as_tuple("Freedom"),
                       forward_as_tuple("gold", "darkblue", "blue", "yellow"));

Чому forward_as_tuple("Freedom") ? , ти ж не дав tuple<string>.
Замість tuple<string, string, string, string> дай vector<string>, ти ж хочеш застосовувати цикл, значить ти мусиш гарантувати що всі елементи є одного типу.

Vitaliy_Danmer написав:

Видає помилку.

Яку ? Код і текст помилки сюди.

Vitaliy_Danmer написав:

Не розумію, чому не можу отримати значення, вказавши ключ.

Для словника (map) можна.

forward_as_tuple("Freedom") — це потрібно для piecewise_construct, бо він, по суті, створює tuple.
Без нього можна було б:

dictcolors.emplace("Freedom", make_tuple("gold", "darkblue", "blue", "yellow"));

Але так будуть зайві операції, як я розумію.
З piecewise_construct краще, бо так створюються нові об'єкти без зайвих копіювань.

Подякували: leofun011

7

Re: map з tuple в якості набору значень

koala написав:
Vitaliy_Danmer написав:

Ось так не працює:

cout << dictcolors["Freedom"] << endl;

error: invalid operands to binary expression ('std::ostream' (aka 'basic_ostream<char>') and 'std::map<std::basic_string<char>, std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>>::mapped_type' (aka 'std::tuple<std::basic_string<char>, std::basic_string<char>, std::basic_string<char>, std::basic_string<char>>'))

Я підкреслив ключові місця. Перекладаю: компілятор не знає, як саме вивести за допомогою оператора << ваш тьюпл у ostream (це тип cout). В C++ не визначена стандартна операція виведення для тьюплів, бо невідомо, як саме вам треба їх виводити. Ви можете (якщо вам це дуже треба) визначити власний operator << для тьюпла, але якщо ви використовуєте конкретний тьюпл так часто, що вам потрібні для нього перевантажені оператори, то варто вже його класом зробити.

Я просто хотів на екран вивести tuple. Надто звик до Пайтона, якщо стисло.

8

Re: map з tuple в якості набору значень

Vitaliy_Danmer написав:

map<string, tuple<string, string, string, string>>

Я б так не робив. tuple<Types...> - сильно допомагають у шаблонному програмуванні та майже завжди програють опису власної структури у всіх інших випадках. Ось це майже завжди гірше:

std::tuple<int, int, int> get_point(...);

За ось це:

struct point3d { ... };
point3d get_point(...);

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

leofun01 написав:

Чому forward_as_tuple("Freedom") ?

Бо конструктор такий викликається:

template< class... Args1, class... Args2 >
constexpr pair(std::piecewise_construct_t,
               std::tuple<Args1...> first_args,
               std::tuple<Args2...> second_args);
Подякували: koala, leofun012

9

Re: map з tuple в якості набору значень

- Мамо, а можна тьюпли як у пайтоні: print(("a", 1, 3.14)[1])?
- Нащо тобі як у пайтоні, у нас вдома тьюпли є!
Тьюпли вдома: std::cout<<std::get<1>(std::make_tuple<std::string, int, float>("a", 1, 3.14));

Подякували: P.Y., leofun012

10 Востаннє редагувалося Teg Miles (20.09.2023 14:36:10)

Re: map з tuple в якості набору значень

Перегляд на власний страх і ризик.

Вікові обмеження 18++
for (const auto &i: dictcolors) {
    string a = get<2>(get<1>(i));
    cout << a << endl;
}

Такий підхід впливатиме на швидкість виконання програми?

11

Re: map з tuple в якості набору значень

string a =

Оце трохи впливатиме. Замініть на const std::string & a. Але це копійки, краще читаність підвищити, а не const & де треба і не треба розставляти.

12

Re: map з tuple в якості набору значень

koala написав:
string a =

Оце трохи впливатиме. Замініть на const std::string &a. Але це копійки, краще читаність підвищити, а не const & де треба і не треба розставляти.

Ось покращений варіант:

for (const auto &[key, data]: dictcolors) {
    const string& a = (get<3>(data));
    cout << a << endl;
}

13

Re: map з tuple в якості набору значень

koala написав:

Але це копійки, краще читаність підвищити

++, раджу покращити саме це, а про оптимізації думати потім.

Vitaliy_Danmer написав:

Ось покращений варіант

Все ще погано, що дістає get<3> все ще не зрозуміло.

14

Re: map з tuple в якості набору значень

wander написав:
koala написав:

Але це копійки, краще читаність підвищити

++, раджу покращити саме це, а про оптимізації думати потім.

Vitaliy_Danmer написав:

Ось покращений варіант

Все ще погано, що дістає get<3> все ще не зрозуміло.

Ось більш зрозуміліший варіант:

for (const auto &[key, data]: dictcolors) {
    string color1, color2, color3, color4;
    tie(color1, color2, color3, color4) = data;
    cout << color4 << endl;
}

Тільки тут уже просто string, а не string&.

15

Re: map з tuple в якості набору значень

Цифри в кінці змінних завжди тхнуть. Чому там саме 4 значення?

Подякували: leofun011

16 Востаннє редагувалося Teg Miles (20.09.2023 17:11:41)

Re: map з tuple в якості набору значень

koala написав:

Цифри в кінці змінних завжди тхнуть. Чому там саме 4 значення?

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

17

Re: map з tuple в якості набору значень

Виявляється, що tie() створює кортеж посилань(&), тому в змінній буде string&.
Ось ще один варіант декомпозиції кортежу:

for (const auto &[key, data]: dictcolors) {
    auto& [color1, color2, color3, color4] {data};
    cout << color4 << endl;
}

Але tie() дещо зручніший, бо там можна ігнорувати змінну через std::ignore.

tie(color1, color2, ignore, color4) = data;

18

Re: map з tuple в якості набору значень

Мені здається, ви не зовсім розумієте, що таке кортежі й з чим їх їдять.
Концептуально std::tuple не сильно відрізняються від plain old struct. Основна відмінність полягає в тому, що доступ до членів структури здійснюється за допомогою імені, а до членів кортежу - за допомогою індексу.
З точки зору реалізації std::tuple - це рекурсивна структура даних, яка зазвичай широко поширена саме у функціональних мовах програмування (привіт Haskell).

Приїхали std::tuple до нас разом з С++11 стандартом, коли й були додані варіативні шаблони, у тому ж стандарті. І це не просто випадковість, бо здебільшого std::tuple були доповненням для зручної роботи саме з варіативними шаблонами. Особливо коли вам необхідно якимсь чином зберегти аргументи варіативного шаблона.

template <typename... Args>
struct dummy_t {
    Args... args; // Так не можна.
};
template <typename... Args>
struct dummy_t {
    std::tuple<Args...> args; // А так ОК.
};

В цьому випадку у вас 2 варіанти або використати std::tuple, або написати власну обгортку, яка швидше за все так чи інакше копіюватиме поведінку кортежу. Тобто тут його використання виправдане і ще є кілька випадків, коли це буде виправдано (більшість з них при шаблонному програмуванні).

У вашому випадку, ви хочете використовувати кортеж як структуру, то чому б не використати просто структуру? Або навіть не як структуру, а як масив. Адже фішка кортежу у тому, що він може зберігати різні типи, у вашому ж випадку вони одні і ті ж. А structured bindings не є виключно прерогативою кортежів, вони працюють з будь-якими агрегатами чи масивами.

struct dummy_t {
    int   i;
    float f;
};

int     a[2] = {1, 2};
dummy_t d    = {5, 3.14f};

auto [x, y] = a; // Ок
auto [z, w] = d; // Ок
Подякували: koala, leofun012

19

Re: map з tuple в якості набору значень

wander написав:

Мені здається, ви не зовсім розумієте, що таке кортежі й з чим їх їдять.
Концептуально std::tuple не сильно відрізняються від plain old struct. Основна відмінність полягає в тому, що доступ до членів структури здійснюється за допомогою імені, а до членів кортежу - за допомогою індексу.
З точки зору реалізації std::tuple - це рекурсивна структура даних, яка зазвичай широко поширена саме у функціональних мовах програмування (привіт Haskell).

Приїхали std::tuple до нас разом з С++11 стандартом, коли й були додані варіативні шаблони, у тому ж стандарті. І це не просто випадковість, бо здебільшого std::tuple були доповненням для зручної роботи саме з варіативними шаблонами. Особливо коли вам необхідно якимсь чином зберегти аргументи варіативного шаблона.

template <typename... Args>
struct dummy_t {
    Args... args; // Так не можна.
};
template <typename... Args>
struct dummy_t {
    std::tuple<Args...> args; // А так ОК.
};

В цьому випадку у вас 2 варіанти або використати std::tuple, або написати власну обгортку, яка швидше за все так чи інакше копіюватиме поведінку кортежу. Тобто тут його використання виправдане і ще є кілька випадків, коли це буде виправдано (більшість з них при шаблонному програмуванні).

У вашому випадку, ви хочете використовувати кортеж як структуру, то чому б не використати просто структуру? Або навіть не як структуру, а як масив. Адже фішка кортежу у тому, що він може зберігати різні типи, у вашому ж випадку вони одні і ті ж. А structured bindings не є виключно прерогативою кортежів, вони працюють з будь-якими агрегатами чи масивами.

struct dummy_t {
    int   i;
    float f;
};

int     a[2] = {1, 2};
dummy_t d    = {5, 3.14f};

auto [x, y] = a; // Ок
auto [z, w] = d; // Ок

Думав, що кортежі простіші й працюють швидше за структури.
Бо структура — це той же клас тільки з публічними стандартними налаштуваннями.
І не дуже хочеться створювати зайвий новий клас, коли є вже готові.
Хоча, можливо, й доведеться.