0

Let's say I have the following code:

#include <iostream>

struct NotCopyable
{
    std::string value;

    NotCopyable(const std::string& str) :
        value(str)
    {
    }
    
    // This struct is not allowed to be copied.
    NotCopyable(const NotCopyable&) = delete;
    NotCopyable& operator =(const NotCopyable&) = delete;
};

struct Printable
{
    NotCopyable& nc;

    // This struct is constructed with a reference to a NotCopyable.
    Printable(NotCopyable& inNc) :
        nc(inNc)
    {
    }

    // So that std::cout can print us:
    operator const char*() const
    {
        return nc.value.c_str();
    }
};

// This function constructs an object of type T,
// given some arguments, and prints the object.
template<typename T, typename... ARGS>
void ConstructAndPrint(ARGS... args)
{
    T object(std::forward<ARGS>(args)...);
    std::cout << "Object says: " << object << std::endl;
}

int main(int, char**)
{
    // Create a NotCopyable.
    NotCopyable nc("123");

    // Try and construct a Printable to print it.
    ConstructAndPrint<Printable>(nc);    // "Call to deleted constructor of NotCopyable"
    
    // OK then, let's make absolutely sure that we're forwarding a reference.
    NotCopyable& ncRef = nc;

    // Try again.
    ConstructAndPrint<Printable>(ncRef); // "Call to deleted constructor of NotCopyable"
}

It seems that the NotCopyable isn't being forwarded properly as a reference, even when I explicitly supply a reference variable as the argument. Why is this? Is there any way I can ensure that a reference is forwarded, and not a copy-constructed object?

NoodleCollie
  • 855
  • 7
  • 24
  • 2
    You have to use forwarding references (`&&`). I mean `void ConstructAndPrint(ARGS && ... args)`. – max66 Apr 09 '21 at 12:01
  • basically a dupe of [When to use std::forward to forward arguments?](https://stackoverflow.com/questions/7257144/when-to-use-stdforward-to-forward-arguments) – underscore_d Apr 09 '21 at 14:08
  • It sort of is a duplicate, but I wouldn't have been able to find that article (and indeed didn't find it) because it's presented in terms of the language technicalities rather than the nature of the forwarded argument. What I really needed info on was the fact that a "forwarding reference" is a thing, rather than `std::forward` which I did know about. – NoodleCollie Apr 09 '21 at 14:20

1 Answers1

1
template<typename T, typename... ARGS>
void ConstructAndPrint(ARGS... args);

take argument by value, so does copy.

You want forwarding reference instead:

// This function constructs an object of type T,
// given some arguments, and prints the object.
template<typename T, typename... ARGS>
void ConstructAndPrint(ARGS&&... args)
{
    T object(std::forward<ARGS>(args)...);
    std::cout << "Object says: " << object << std::endl;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302