13

I'm trying to make a movable wrapper to non-copyable, non-movable class, however I have a problem passing a const std::string variable to the constructor. The minimal example below produces following error:

#include <iostream>
#include <memory>
#include <string>
#include <utility>

struct X {
    std::string x;

    X(const std::string &x) : x(x) {}
    X(const X &x) = delete;
    X(X &&x) = delete;
};

struct Wrapper {
    std::unique_ptr<X> x;

    Wrapper(const Wrapper & wrapper) = delete;
    Wrapper(Wrapper && wrapper) = default;

    template<typename... Args>
    Wrapper(Args&&... args) : x(std::make_unique<X>(std::forward(args)...)) {}
};

int main() {
    const std::string XXX = "XXX";
    Wrapper w{XXX};
    std::cout << w.x->x << std::endl;
}

Error message here:

forwarding.cc:21:53: error: no matching function for call to 'forward'
    Wrapper(Args&&... args) : x(std::make_unique<X>(std::forward(args)...)) {}
                                                    ^~~~~~~~~~~~
forwarding.cc:26:13: note: in instantiation of function template specialization 'Wrapper::Wrapper<const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > &>' requested here
    Wrapper w{XXX};
            ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/move.h:73:5: note: candidate template ignored: couldn't infer template argument '_Tp'
    forward(typename std::remove_reference<_Tp>::type& __t) noexcept
    ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/7.2.0/../../../../include/c++/7.2.0/bits/move.h:84:5: note: candidate template ignored: couldn't infer template argument '_Tp'
    forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    ^
1 error generated.
Erbureth
  • 3,378
  • 22
  • 39
  • 7
    AFAIK forward need to get template argument passed explicitly in your case `std::forward(args)...` – W.F. Jan 29 '18 at 12:58
  • 2
    To complement Vittorio's great answer, if you want to know how template argument deduction is turned off for `std::forward`, resulting in an error and not weird behavior, then you can read [this](https://stackoverflow.com/questions/37418089/how-remove-reference-disable-template-argument-deductions). – StoryTeller - Unslander Monica Jan 29 '18 at 13:10

1 Answers1

34

You need to explicitly pass template parameters to std::forward:

std::forward<Args>(args)...

This is because std::forward needs some way of knowing the "original value category" of args..., which is impossible through template argument deduction alone as args is always an lvalue.

Lvalues will deduced as lvalue references in the context of template argument deduction for forwarding references (as a special rule), so std::forward can do its job by looking at the types inside Args....

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • Thanks, I knew, I was missing something like that, but I didn't know, how to use it with parameter pack – Erbureth Jan 29 '18 at 13:03