11
#include <iostream>

class NoCopyMove {
public:
    NoCopyMove(int a) : a_(a), b_(a) {}
    NoCopyMove(int a, int b) : a_(a), b_(b) {}

    NoCopyMove(const NoCopyMove&) = delete;
    NoCopyMove& operator=(const NoCopyMove&) = delete;
    NoCopyMove(NoCopyMove&&) = delete;
    NoCopyMove& operator=(NoCopyMove&&) = delete;

    int a_;
    int b_;
};

int main()
{
    std::tuple<NoCopyMove, NoCopyMove> t {6, 9};
    std::cout << std::get<0>(t).a_ << std::endl;   
    std::tuple<NoCopyMove, NoCopyMove> t2 {{6, 7}, {8, 9}};
    return 0;
}

I'm trying to make a tuple of classes that has more than 2 arguments as their constructor. If there is just one constructor argument it works.

main.cpp:45:28: error: no matching constructor for initialization of 'std::tuple<NoCopyMove>'
    std::tuple<NoCopyMove> t2 {{6, 7}, {8, 9}}};
                           ^  ~~~~~~~~~~~~~~~~

Probably some kind of hint to the compiler would be needed but I have no idea how I could do that. Any kind of keyword and hint will be appreciated.

Daniel Lee
  • 205
  • 3
  • 7
  • 4
    A side note: I think you have a typo: `{8, 9}}};` should be `{8, 9}};` (i.e. only 2 `}`). – wohlstad Feb 28 '23 at 08:42
  • 1
    Also from the compiler error it seems you are instantiating a tuple with only one element of type `NoCopyMove`. – chrysante Feb 28 '23 at 08:45
  • Tuple is not an aggregate, so it can't be initialized using aggregate initialization, and when you look at expression {{6, 7}, {8, 9}} - it probably have type something like `std::initializer_list>` or something similar which doesn't match nor `tuple` nor your constructor. – sklott Feb 28 '23 at 08:53
  • 1
    Just one excessive closing brace. Remove it, and report back please. – Red.Wave Feb 28 '23 at 10:06
  • 1
    Side note: purpose of tuple is to store some data for later in generic programing. **IMPO** using tuples outside of templates makes code harder to read and maintain. So from that point of view if you have some generic code which needs keep data for later use, then your class you have problem with (`NoCopyMove`) do not meet requirements of this generic code. If you do not have generic code, then define struct which will be tailored to store and construct `NoCopyMove` with multiple arguments. – Marek R Feb 28 '23 at 10:26

2 Answers2

12

Aside from the extraneous closing brace, there is no way you can construct a tuple of uncopyable and immoveable types like this. std::tuple does not support piecewise or 'emplace-style' construction and as mentioned in the comments it is also not an aggregate, so it needs to copy or move the individual elements in place. The constructor you would expect to be chosen here is the one which takes each type in the tuple by const & and quoting cppreference:

This overload participates in overload resolution only if sizeof...(Types) >= 1 and std::is_copy_constructible::value is true for all i.

However your types are not copy constructible, so you are out of luck if this really is what you need to do.

chrysante
  • 2,328
  • 4
  • 24
9
std::tuple<NoCopyMove, NoCopyMove> t2 {{6, 7}, {8, 9}}};

is actually

std::tuple<NoCopyMove, NoCopyMove> t2 {NoCopyMove{6, 7}, NoCopyMove{8, 9}}};

and so requires move or copy constructor.

In C++17, with mandatory copy elision, with extra layer

template <typename... Ts>
struct Args
{
    Args(Ts... args) : tuple{std::forward<Ts>(args)...} {}

    template <typename T>
    operator T() && { return std::make_from_tuple<T>(std::move(tuple)); }

private:
    std::tuple<Ts...> tuple;
};

template <typename... Ts>
Args(Ts&&...) -> Args<Ts&&...>;

you might do:

std::tuple<NoCopyMove, NoCopyMove> t2 {Args{6, 7}, Args{8, 9}}};

Demo

And so you construct in place NoCopyMove from Args{..}.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    Note that this question is tagged C++14, but this answer requires at least C++17 for `make_from_tuple`, CTAD, and mandatory prvalue copy elision. – Miles Budnek Feb 28 '23 at 21:54
  • @MilesBudnek: indeed. whereas `make_from_tuple` can be reimplemented, and CTAD replaced by `makeArgs`, there are no replacement for mandatory prvalue copy elision :/ – Jarod42 Mar 01 '23 at 14:27