c++referenceperfect-forwardingstdtuplereference-collapsing

reference collapsing and and tuples


I'm trying to convert a parameter pack into references, because some arguments to my function can be a mix of r-/l- values. The function in question:

//must return tuple
template <typename ...U>
std::tuple<U...> input(const char* format, U ...args) {
    std::tuple<U...> t = std::tuple<U...> (args...);
    //other code....
}

There is some test code that I can't touch... This will call my function:

template <typename... U>
std::tuple<U...> test_input(const char* fmt, U &&...inp) {
    input(fmt, std::forward<U>(params)...);
    //other stuff...
}

And 2 test objects (also untouchable) which have deleted copy/move constructors A() and B(). as in:

A(const A &) = delete;            //same for B
A &operator=(const A &) = delete; //same for B

If I call the function, as is, I'll get "deleted copy-constructor" or "deleted constructor" errors. For example:

test_input("blah blah", 1, i, "a", std::string("a"), A(123) B("string"));

The problem is that it can be any mix of r-/l-values and I don't know how to convert them to all be references

I understand that I need a reference to the arguments. I've tried using std::forward, std::forward_as_tuple, std::make_tuple, as well as changing the second parameter to input to be U & ...args and U &&...args

I also understand that I need to use reference collapsing:

I tried to use the first and third rules to convert anything to a type of A&, but I still get errors such as: call to deleted constructor of 'B' and expects an l-value for 2nd argument

In case my question wasn't clear - How do I convert args, the second argument of input, to a tuple of references?


Solution

  • I think you want to do something like this:

    #include <tuple>
    #include <string>
    
    //must return tuple
    template <typename ...U>
    std::tuple<U&&...> input(const char*, U&&...args) {
        return std::tuple<U&&...>(std::forward<U>(args)...);
        //other code....
    }
    
    template <typename... U>
    std::tuple<U&&...> test_input(const char* fmt, U &&...inp) {
        return input(fmt, std::forward<U>(inp)...);
        //other stuff...
    }
    
    struct A {
        A(int) { }
        A(const A &) = delete;            //same for B
        A &operator=(const A &) = delete; //same for B
    };
    
    struct B {
        B(const char *) { }
        B(const B &) = delete;            //same for B
        B &operator=(const B &) = delete; //same for B
    };
    
    int main() {
        int i = 1;
        test_input("blah blah", 1, i, "a", std::string("a"), A(123), B("string"));
    }
    

    [live demo]