std :: tuple, дубликат типа T в get <T>(tuple) - ошибка подтверждения времени компиляции - PullRequest
2 голосов
/ 11 июля 2019

Я храню переменные аргументы в конструкторе объектов внутри std::tuple и пока все хорошо.Но при вызове функции объекта с использованием сохраненных аргументов и std::get<>() мне будет выдан сбой утверждения время компиляции , которого я просто не понимаю.Это произойдет только в том случае, если все аргументы не принадлежат разным типам .

Сообщение об ошибке компилятора:

msvc \ 14.16.27023 \ include \кортеж (934): ошибка C2338: дубликат типа T в get (кортеж)

mcve ниже:

#include <tuple>
#include <iostream>

using namespace std;

template<class... Args>
struct store_in_tuple {

    tuple<Args...> m_tuple_args;

    store_in_tuple(Args... args) : m_tuple_args{ args... } {}

    void func() {
        func_tuple(std::get<Args>(m_tuple_args)...);
    }

    void func_tuple(Args... args) {}
};

int main(int argc, char** argv) {

    store_in_tuple<int, float, double, int> sit1(1, 2.0f, 3.0, 4);
    sit1.func(); // <- not ok

    store_in_tuple<int, float, double, size_t> sit2(1, 2.0f, 3.0, 4);
    sit2.func(); // <- ok

    return 0;
}

Почему это происходит и есть ли обходной путь?

Ответы [ 3 ]

3 голосов
/ 11 июля 2019

Пример можно упростить до следующего:

auto t = std::make_tuple(1, 's', 2);
std::get<int>(t);

здесь у нас есть t типа std::tuple<int, char, int>. std::get также может работать с типами (наряду с индексами), если у вас нет дублирующего типа. std::get<char> будет работать, поскольку в t есть только один char, но std::get<int> не будет работать, поскольку он не знает , который int извлечь - 1 или 2?

Вот что здесь происходит:

void func() {
    func_tuple(std::get<Args>(m_tuple_args)...);
}

std::get<Args> после раскрытия не будет работать, если Args... содержит хотя бы один дубликат типа, потому что он просто не будет знать , который один для извлечения .

2 голосов
/ 11 июля 2019

Используйте C ++ 17 std::apply() для передачи всех элементов кортежа в функцию.

std::apply([&](auto... x){ func_tuple(x...); }, m_tuple_args);

Вы настаиваете на том, чтобы остаться с C ++ 14?
Нет проблем, cppreference.com демонстрирует краткий и простой пример реализации производственного качества.

Кроме того, вы можете напрямую работать с std::make_index_sequence, чтобы получать уникальные индексы вместо дублированных типов.

1 голос
/ 11 июля 2019

Почему это происходит [?]

Все идет хорошо, когда все типы Args... различны.

Вы получаете ошибку, когда типы сталкиваются.

Это потому, что std::get<T>(tuple_val), где T - это тип, «не может скомпилироваться, если в кортеже нет только одного элемента этого типа» (как вы можете прочитать в на этой странице ),И это кажется мне разумным.

Так что все идет хорошо с

store_in_tuple<int, float, double, size_t> 

, потому что все типы различны, и вы получаете ошибку от

store_in_tuple<int, float, double, int>

, потому чтодва вызова std::get<int>(m_tuple_args) завершились неудачно.

и есть ли обходной путь?

Используйте числовую версию std::get(), которая всегда доступна, даже когда типы сталкиваются.

Обычный способ в C ++ 14 проходить через вспомогательную функцию с std::index_sequence и std::make_index_sequence (или std::index_sequence_for).

Кажется сложным, но очень простым

template <std::size_t ... Is>
void func_helper (std::index_sequence<Is...> const)
 { func_tuple(std::get<Is>(m_tuple_args)...); }

void func ()
 { func_helper(std::index_sequence_for<Args...>{}); }

Если вы можете использовать C ++ 17, вы можете использовать std::apply(), который (я полагаю) использует std::index_sequence под капотом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...