Initialize only first argument of c++11 tuple

1.4k views Asked by At

Minimal working example:

#include <tuple>

struct example
{
    example(int, char) {}
};

int main()
{
    std::tuple<example, int, double>
        my_tuple(example(0, 'x'), 42, .0);
    // std::tuple t = make_my_tuple(0, 'x');
    return 0;
}

This works.

Is there a more elegant way to initialize only the first member, like I sketched in the comment? One which only takes the arguments to construct the first tuple member and does not initialize the others?

The reason I ask? I am just interested in the semantics of the language.

2

There are 2 answers

3
AudioBubble On

You say that giving values for the other two members is not necessary - are you worried about performance? Or that there may be no suitable value for these members?

If it's the latter, you could have your tuple hold boost::optionals. e.g.

#include <tuple>
#include <boost/optional.hpp>

using namespace boost;

struct example
{
    example(int, char) {}
};

typedef std::tuple<example, optional<int>, optional<double>> MyTuple;

int main()
{
   MyTuple my_tuple(example(0, 'x'), optional<int>(), optional<double>());
   return 0;
}

You now semantically have the int and float "uninitialised", and can query their value as such.

To make this more elegant, you can wrap this into a function, using the perfect forwarding idiom for the arguments (in general; in this case your arguments are cheap to copy, so no speed benefit from doing this):

template <class... Args>
MyTuple make_mytuple(Args&&... args)
{
    return MyTuple(example(std::forward<Args>(args)...), optional<int>(), optional<double));
}

The advantage of this template is that it's resilient to changes in example's constructor. If you add another argument, just call make_mytuple with the new arguments and it will work.

Your other point about the copying in the tuple construction is valid, but in reality I believe this will be optimal on most compilers. (a combination of RVO and elision of copies when passing an rvalue to a function by value).

0
Serge Glazomitsky On

You can use uniform initialization. Sadly, you cannot define a default value, argument will be initialized with the default constructor or 0.

#include <iostream>
#include <tuple>

enum class Result {Full, Partial, Empty};
std::tuple<bool, int, double> get_tuple(Result type)
{
    if (type == Result::Full)
        return {true, 42, 3.14159};
    else if (type == Result::Partial)
        return {true, 42, {}};
    else
        return {};
}

int main()
{
    bool b;
    int i;
    double d;

    std::tie(b, i, d) = get_tuple(Result::Full);
    std::cout << b << " " << i << " " << d << std::endl;

    std::tie(b, i, d) = get_tuple(Result::Partial);
    std::cout << b << " " << i << " " << d << std::endl;

    std::tie(b, i, d) = get_tuple(Result::Empty);
    std::cout << b << " " << i << " " << d << std::endl;

    return 0;
}

output:

1 42 3.14159
1 42 0
0 0 0