0
#include <utility>
#include <vector>
#include <cstdint>

template <typename T>
struct Base
{
protected:
    Base(T&& data):
        data(std::forward(data)){
    }
    virtual ~Base(){};

public:

    T getData() const {return data;}

    void setData(T&& data) {
       this->data = std::forward(data);
    }
private:
    T data;
};


struct DerivedA: public Base<int>
{
public:
    DerivedA(int data):
        Base(std::move(data)){//Should I use std::forward instead of std::move here?
    }
};

struct DerivedB: public Base<const std::vector<uint16_t> >
{
public:
    DerivedB(const std::vector<uint16_t>& data):
        Base(std::move(data)){
    }
};

My requirements is to have 0 copying of objects when creating the Derived Classes above. But no matter how I write the above I get compiler errors, these are the latest:

bin/Base.h: In instantiation of ‘Base<T>::Base(int, int, int, T&&) [with T = int]’:
bin/Base.h:33:82:   required from here
bin/Base.h:12:96: error: no matching function for call to ‘forward(int&)’
/usr/include/c++/4.7/bits/move.h:77:5: note: template<class _Tp> constexpr _Tp&& std::forward(typename std::remove_reference<_Tp>::type&)
/usr/include/c++/4.7/bits/move.h:77:5: note:   template argument deduction/substitution

What am I doing wrong here?

Also, should I do std::move(data) when data in an int or std::forward?

Kam
  • 5,878
  • 10
  • 53
  • 97
  • you have to call it like `forward(data)` – Bryan Chen Dec 01 '14 at 05:06
  • Yes you are correct, that fixed it, but why can't it figure it out compile time? Also, should I do `std::move(data)` when `data` is an `int` or `std::forward`? – Kam Dec 01 '14 at 05:13
  • possible duplicate of [Why is template argument deduction disabled with std::forward?](http://stackoverflow.com/questions/7779900/why-is-template-argument-deduction-disabled-with-stdforward) – Bryan Chen Dec 01 '14 at 05:16
  • for `int`, just pass by value... also `DerivedB` can make use of move because it takes const reference. you need constructor takes rvalue ref – Bryan Chen Dec 01 '14 at 05:16
  • 1
    The `T&&` in `Base` aren't forwarding references; they are rvalue references. Also, the thing you do to an `int` is irrelevant. For such a primitive type, move == copy. – T.C. Dec 01 '14 at 05:16
  • @T.C. T&& is a universal reference no? – Kam Dec 01 '14 at 05:21
  • @Kam Only in a deduced context. Neither `Base::Base(T&&)` nor `Base::setData(T&&)` is a function template, so `T` is not being deduced. `T` comes directly from the class template parameters. (Also, [we're calling them "forwarding references" now](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4164.pdf) ;) – Casey Dec 01 '14 at 16:46

1 Answers1

1

If you want to perfectly forward an argument, you'll either need to provide the corresponding three overloads or make the argument a template argument. In either case, when you want to use std::forward() you'll need to specify the first template argument as it is not deduced. Most likely, you'd use something like this:

template <typename T>
class Base {
public:
    template <typename A>
    Base(A&& data): data(std::forward<A>(data)) {}
};

Trying to std::move(data) when data is a std::vector<uint16_t> const& won't move the vector nor will it make the object look like a non-const rvalue: if you want to make the vector movable, you'll need to pass it as non-const reference, an rvalue, or a value. You may also want to deduce the type or overload on std::vector<uint16_t>&& and std::vector<uint16_t>&. For both of these overloads using std::move() does the trick. If you deduce the type, you'll use std::forward<A>(data) again. In case the deduced type looks to scare, you can constrain it using std::enable_if<...> using something like this:

template <typename A>
DerivedB(A&& arg,
         typename std::enable_if<std::is_same<typename std::decay<A>::value,
                                              std::vector<uint16_t>>::value>::type* = 0):
    Base(std::forward<A>(arg)) {
}
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • I know that int will not be moved, but since the move is happening in a base class and I don't want to have template specialization for an int, I std::move it. But should I std::move it or std::forward it? – Kam Dec 01 '14 at 05:20
  • I would do neither: instead I'd use a deduced type in the base class. You'd only need to turn the `int` into an rvalue reference if the base class only has an rvalue reference constructor. In any case, using `std::forward(data)` and `std::move(data)` have the same effect: in both cases, the argument will look like an rvalue. I also see why you used `std::move(data)` for the `int` - I'll adjust the answer... – Dietmar Kühl Dec 01 '14 at 05:28
  • Thank you, what do you mean deduction type in base class, I have never heard of this, can you give an example please? – Kam Dec 01 '14 at 05:30
  • I did already gave an example: the constructor of `Base` in the answer is a member template deducing the argument type and the exact kind of type (rvalue, lvalue, or `const` lvalue). – Dietmar Kühl Dec 01 '14 at 05:33
  • 1- In your example why can't we use `template ` instead of redefining another template `template ` in the base class? 2- you mentioned that "Trying to `std::move(data)` when data is a `std::vector const&` won't move the vector" why is that? – Kam Dec 01 '14 at 05:39
  • 1. The template argument for the class is obviously fixed and won't change depending on the way constructor arguments are passed. I think you can use the name `T` on the constructor but it would be confusing to use one name to mean two different things. 2. You can't move from a `const` vector as movung the content means to change the dource. However, the source is `const` and can't be changed, i.e., it will have to copy. – Dietmar Kühl Dec 01 '14 at 13:36