Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am storing the variadic arguments to an object constructor inside a std::tuple and so far so good. But when calling an object function using the stored arguments and std::get<>() , I will be thrown a compile-time assertion failure that I simply don't understand. This will happen only when all arguments are not each of a different type .

The compiler error message is:

msvc\14.16.27023\include\tuple(934): error C2338: duplicate type T in get(tuple)

mcve below:

#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;

Why does this happen and is there a workaround ?

here, we have a t of a type std::tuple<int, char, int>. std::get can also work with types (alongside indexes), unless you have a duplicate type. std::get<char> will work since there is only one char in the t, but std::get<int> will not work, since it does not know which int to fetch - the 1 or the 2?

This is what's happening here:

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

the std::get<Args>, after expansion, will not work if Args... contain at least one duplicate type, because it will simply not know which one to fetch.

Use C++17 std::apply() to pass all the elements of a tuple to a function.

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

You insist on staying with C++14?
No problem, cppreference.com shows a short and simple production-quality example-implementation.

Alternatively, you can work directly with std::make_index_sequence to get unique indices instead of duplicated types.

It worked for me using a helper func and index_sequence_for. Thanks! I think when using std::apply for a class instance method, one has to prepend the actual instance to the tuple so that it becomes the 1st argument to the function/callable - but I couldnt afford that extra copy ;) – PinkTurtle Jul 11, 2019 at 18:54

All goes well when the Args... types are all differents.

You get an error when types collides.

This is because std::get<T>(tuple_val), where T is a type, "Fails to compile unless the tuple has exactly one element of that type" (as you can read in this page). And this seems reasonable to me.

So all goes well with

store_in_tuple<int, float, double, size_t> 

because all types are differents, and you get an error from

store_in_tuple<int, float, double, int>

because the two calls to std::get<int>(m_tuple_args) fail.

and is there a workaround ?

Use the numeric version of std::get(), that is ever available, also when types collides.

The usual way in C++14 pass through an helper function with std::index_sequence and std::make_index_sequence (or std::index_sequence_for).

Seems complicated but it's very simple

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...>{}); }

If you can use C++17, you can use std::apply() that (I suppose) use std::index_sequence under the hood.

Thanks, this clarifies the use of std::index_sequence for me. I don't want to use a helper function tho so I will use C++17's apply or.... you meationed the numeric version of std::get(), what does this mean and can I unwrap a parameters pack with it in C++14? Thanks! Edit: just read the doc no need to answer :] – PinkTurtle Jul 10, 2019 at 22:54 @PinkTurtle - for "numeric version of std::get()" I mean the version of std::get<>() that receive a std::size_t, instead of a type, as template parameter (cases from (1) to (4) in this page. I don't full understand your following question but you can see the way to unpack a parameter pack in C++11 (and newer versions) is the usual: you can see it in func_helper(). The problem, here, is generate the parameter pack of indexes; you need std::make_index_sequence or std::index_sequence_for (that are almost the same). (continue) – max66 Jul 10, 2019 at 23:01 @PinkTurtle - (continue) Unfortunately std::index_sequence are available only starting from C++14 and I don't see a way to generate the index sequence without defining an helper functions. Starting from C++17, you can use std::apply() that take care of generate the index sequence and use std::get<>(), numeric version. – max66 Jul 10, 2019 at 23:04

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.