Search code examples
c++templatesoverload-resolution

How to deal with const T& and T&& instantiating to the same signature?


Basically I want to provide construction from const lvalue versions and rvalue reference versions:

    transform_iterator(const Functor& f, const Iterator& it) :
            functor(f),
            iterator(it)
    {}

    transform_iterator(Functor&& f, Iterator&& it) :
            functor(f),
            iterator(it)
    {}

From what I know, the rvalue reference version is not universal references. Despite that, on this call site:

template <typename InputIt, typename OutputIt>
std::pair<InputIt, OutputIt> sliding_average(InputIt first, InputIt last,
                    const typename std::iterator_traits<InputIt>::difference_type window_length,
                    OutputIt d_first)
{
    using value_type = typename std::iterator_traits<InputIt>::value_type;
    auto divide = [&window_length](const value_type& value)
    {
        return value / window_length;
    };

    auto iterator = shino::transformer(divide, d_first); //transform_iterator<Functor, Iterator>

   shino::sliding_window(first, last, iterator, window_length);
                                       //^^ pass by value

compiler says that rvalue reference version ends up being const lvalue reference version.

For completeness, here is the call site of the sliding_average

sliding_average(v.begin(), v.end(), window_length, output.begin());

where both v and output are vectors of int and doubles, and window_length is std::size_t.

When I remove the const lvalue version, code compiles and works correctly.

Also, this code compiles without problem with both of the constructors:

std::vector<std::string> v{"23", "25", "27"}; //just random numbers
std::vector<int> output(v.size());

auto string_to_int = [](const std::string& x)
{
    return std::stoi(x);
};

auto conversion_iterator = shino::transformer(string_to_int, output.begin());

Question: How to fix it?


Solution

  • I think that your understanding of lvalues and rvalues is not correct.

    You're not invoking transform_iterator::transform_iterator with an rvalue anywhere in the code you've posted.


    auto iterator = shino::transformer(divide, d_first);
    //                                         ^^^^^^^
    //                                         lvalue
    

    To invoke the rvalue reference overload, you need an rvalue! std::move can be used to cast d_first to an rvalue reference - example:

    auto iterator = shino::transformer(divide, std::move(d_first));
    //                                         ^^^^^^^^^^^^^^^^^^
    //                                         rvalue
    

    The same applies to the code you added in your edit:

    shino::sliding_window(first, last, iterator, window_length);
    //                                 ^^^^^^^^
    //                                 lvalue
    

    You probably want:

    shino::sliding_window(first, last, std::move(iterator), window_length);
    //                                 ^^^^^^^^^^^^^^^^^^^
    //                                 rvalue
    

    Relevant questions: