1

I'm getting compiler error when using std::forward on a class template parameter, but not on a function template parameter. I'd like to know how to std::forward a class template parameter. This is my code:

#include <iostream>
#include <vector>

template<typename T>
class Data_List {
    std::vector<T> data_list;
    
public:
    Data_List() = default;
    ~Data_List() = default;
    
    std::size_t get_list_size() {
        return data_list.size();
    }
    
    void add_to_list(T&& data) {
        std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
        data_list.push_back(std::forward<T>(data));
        std::cout << "after inside add_to_list: " << data.size() << std::endl;
    }
};

template<typename T>
void print(T&& t) {
    
}

int main() {
    Data_List<std::vector<int>> list_of_data;

    std::vector<int> data(100, 2);
    std::cout << "\n1. before size: " << data.size() << std::endl;
    std::cout << "1. before Data_List size: " << list_of_data.get_list_size() << std::endl;
    print(data);
    // list_of_data.add_to_list(data); // gives compiler error here
    std::cout << "\n1. after size: " << data.size() << std::endl;
    std::cout << "1. after Data_List size: " << list_of_data.get_list_size() << std::endl;
    
    std::cout << "--------------------------------------------------------------------------\n" << std::endl;
    
    std::cout << "\n2. before size: " << data.size() << std::endl;
    std::cout << "2. before Data_List size: " << list_of_data.get_list_size() << std::endl;
    print(std::move(data));
    list_of_data.add_to_list(std::move(data));
    std::cout << "\n2. after size: " << data.size() << std::endl;
    std::cout << "2. after Data_List size: " << list_of_data.get_list_size() << std::endl;
    
    std::cout << "--------------------------------------------------------------------------\n" << std::endl;
}

This is the error I'm getting cannot bind rvalue reference of type ‘std::vector<int>&&’ to lvalue of type ‘std::vector<int>’

Harry
  • 2,177
  • 1
  • 19
  • 33
  • what error? Please include the error message in the question – 463035818_is_not_an_ai Sep 27 '21 at 14:03
  • Since you have specified the `T` as `std::vector`, the `T&&` in `add_to_list(T&& data)` will be instantiated as `std::vector&&`, it is an rvalue reference and you cannot bind it to an lvalue. – 康桓瑋 Sep 27 '21 at 14:10
  • @francesco: You have to uncomment the problematic line [Demo](https://godbolt.org/z/1f7TaG5or). – Jarod42 Sep 27 '21 at 14:12
  • @Jarod42 that's just to see whether I'd get same error in case of template function – Harry Sep 27 '21 at 14:17
  • @康桓瑋 but how is it different from `print` template function? – Harry Sep 27 '21 at 14:18
  • About `print` it's called forwarding reference https://stackoverflow.com/q/39552272/12416453 – Ch3steR Sep 27 '21 at 14:20
  • You would have same issue with `print>`, but `print(std::move(data))` resolve into `print&&>`. In the same way `Data_List&&>` would allow your call (except that `std::vector` of reference is wrong). – Jarod42 Sep 27 '21 at 14:21

1 Answers1

6

In here:

void add_to_list(T&& data) {
    std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
    data_list.push_back(std::forward<T>(data));
    std::cout << "after inside add_to_list: " << data.size() << std::endl;
}

data isn't a forwarding reference, it's an rvalue reference, so you can't do something like list_of_data.add_to_list(data); because data is an lvalue.

To create a forwarding reference, the type must exist as a template parameter of the same function template (Give a reading to the forwarding references part of this cppreference.com page and look specifically at the first example):

  1. function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template.

Basically, what you want to do is this:

// ...
template <typename U>
void add_to_list(U&& data) {
    std::cout << "\nbefore inside add_to_list: " << data.size() << std::endl;
    data_list.push_back(std::forward<U>(data));
    std::cout << "after inside add_to_list: " << data.size() << std::endl;
}
// ...

Then data becomes a forwarding reference and does what is intended.

Demo

Ruks
  • 3,886
  • 1
  • 10
  • 22
  • I agree with that answer. The proposed solution is rather general. The API is now extended by function calls that will compile whenever T is copy/move constructable by U&&. – Mehno Sep 27 '21 at 14:42