-2

What is the exact difference of universal reference and rvalue reference?

The code below should except both rvalue reference and lvalue reference as argument. And apparently the first function for_each takes both successfully. However, when it calls the internal function min_iterable_length, it does not work as expected. It seems that the internal function is expecting only a rvalue reference not lvalue reference.

What is the difference? Apparently the first function for_each did take lvalue reference as legal argument. And I used std::forward to pass it exactly to the second function. And why is the second function only accept rvalue reference?

#include <array>
#include <vector>
#include <utility>    

template <typename A, size_t N>
size_t min_iterable_length(A(&&)[N])
{
    return N;
}

template <typename A, size_t N>
size_t min_iterable_length(std::array<A, N> &&)
{
    return N;
}

template <typename A>
size_t min_iterable_length(std::vector<A> &&vector)
{
    return vector.size();
}

template <typename A, size_t N, typename... Args>
size_t min_iterable_length(A (&&head)[N], Args &&...tail)
{
    size_t n_t = min_iterable_length(std::forward<Args>(tail)...);
    return N < n_t ? N : n_t;
}

template <typename A, size_t N, typename... Args>
size_t min_iterable_length(std::array<A, N> &&head, Args &&...tail)
{
    size_t n_t = min_iterable_length(std::forward<Args>(tail)...);
    return N < n_t ? N : n_t;
}

template <typename A, typename... Args>
size_t min_iterable_length(std::vector<A> &&head, Args &&...tail)
{
    size_t n_h = head.size();
    size_t n_t = min_iterable_length(std::forward<Args>(tail)...);
    return n_h < n_t ? n_h : n_t;
}

template <typename F, typename... Args>
void for_each(F f, Args &&...args)
{
    size_t length = min_iterable_length(std::forward<Args>(args)...);

    for (int i = 0; i < length; ++i)
    {
        f(args[i]...);
    }

    return;
}

int c_array_3[3] = {1, 2, 3};
int res_1 = 0;
auto add_1 = [&](int x)
{
    res_1 += x;
};

for_each(add_1, c_array_3);
[build] /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:32:5: note: in instantiation of function template specialization 'for_each<(lambda at /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:27:18), std::array<double, 3> &>' requested here
[build]     for_each(add_2, std_array_3);
[build]     ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:22:8: note: candidate function [with A = double, N = 3] not viable: expects an rvalue for 1st argument
[build] size_t min_iterable_length(std::array<A, N> &&)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:41:8: note: candidate function [with A = double, N = 3, Args = <>] not viable: expects an rvalue for 1st argument
[build] size_t min_iterable_length(std::array<A, N> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:16:8: note: candidate template ignored: could not match 'A[N]' against 'std::array<double, 3>'
[build] size_t min_iterable_length(A(&)[N])
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:28:8: note: candidate template ignored: could not match 'vector' against 'array'
[build] size_t min_iterable_length(std::vector<A> &&vector)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:34:8: note: candidate template ignored: could not match 'A[N]' against 'std::array<double, 3>'
[build] size_t min_iterable_length(A (&head)[N], Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:48:8: note: candidate template ignored: could not match 'vector' against 'array'
[build] size_t min_iterable_length(std::vector<A> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:58:21: error: no matching function for call to 'min_iterable_length'
[build]     size_t length = min_iterable_length(std::forward<Args>(args)...);
[build]                     ^~~~~~~~~~~~~~~~~~~
[build] /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:41:5: note: in instantiation of function template specialization 'for_each<(lambda at /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:36:18), std::vector<double> &>' requested here
[build]     for_each(add_3, std_vector_3);
[build]     ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:28:8: note: candidate function [with A = double] not viable: expects an rvalue for 1st argument
[build] size_t min_iterable_length(std::vector<A> &&vector)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:48:8: note: candidate function [with A = double, Args = <>] not viable: expects an rvalue for 1st argument
[build] size_t min_iterable_length(std::vector<A> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:16:8: note: candidate template ignored: could not match 'A[N]' against 'std::vector<double>'
[build] size_t min_iterable_length(A(&)[N])
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:22:8: note: candidate template ignored: could not match 'array' against 'vector'
[build] size_t min_iterable_length(std::array<A, N> &&)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:34:8: note: candidate template ignored: could not match 'A[N]' against 'std::vector<double>'
[build] size_t min_iterable_length(A (&head)[N], Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:41:8: note: candidate template ignored: could not match 'array' against 'vector'
[build] size_t min_iterable_length(std::array<A, N> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:58:21: error: no matching function for call to 'min_iterable_length'
[build]     size_t length = min_iterable_length(std::forward<Args>(args)...);
[build]                     ^~~~~~~~~~~~~~~~~~~
[build] /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:50:5: note: in instantiation of function template specialization 'for_each<(lambda at /Users/username/Documents/dev/arduino/libraries/efp/test/prelude_test.cpp:45:24), std::vector<double> &, int (&)[3]>' requested here
[build]     for_each(add_product, std_vector_3, c_array_3);
[build]     ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:48:8: note: candidate function [with A = double, Args = <int (&)[3]>] not viable: expects an rvalue for 1st argument
[build] size_t min_iterable_length(std::vector<A> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:34:8: note: candidate template ignored: could not match 'A[N]' against 'std::vector<double>'
[build] size_t min_iterable_length(A (&head)[N], Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:41:8: note: candidate template ignored: could not match 'array' against 'vector'
[build] size_t min_iterable_length(std::array<A, N> &&head, Args &&...tail)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:28:8: note: candidate function template not viable: requires single argument 'vector', but 2 arguments were provided
[build] size_t min_iterable_length(std::vector<A> &&vector)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:22:8: note: candidate function template not viable: requires 1 argument, but 2 were provided
[build] size_t min_iterable_length(std::array<A, N> &&)
[build]        ^
[build] /Users/username/Documents/dev/arduino/libraries/efp/./src/prelude.hpp:16:8: note: candidate function template not viable: requires 1 argument, but 2 were provided
[build] size_t min_iterable_length(A(&)[N])
lighthouse
  • 413
  • 2
  • 10
  • 1
    It compiles well for me when I add the missing `#include #include #include `. Please be kind to post a [mcve], don't make SO users to guess and modify your code so they eliminate the issue. – 273K Jul 08 '23 at 04:15
  • @273K I have added some includes function calling part. Thank you for the comment – lighthouse Jul 08 '23 at 04:24
  • There is the error irrelevant to the errors in the question: `error: non-local lambda expression cannot have a capture-default`. Please copy paste the posted code, compile it on your PC and ensure the shown issue is replicable with the posted code. – 273K Jul 08 '23 at 04:29
  • Why do you want to accept forwarding or rvalue references? These functions never copy or move their argument. A const lvalue reference would work just fine, and you get rid of noisy `std::forward`. – n. m. could be an AI Jul 08 '23 at 06:17

1 Answers1

0

Neither of the min_iterable_length functions are declared to accept forwarding reference for their first argument, they all accept their first argument as an rvalue reference.

Forwarding references are either:

  1. function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template
  2. auto&& except when deduced from a brace-enclosed initializer list

Your code can be corrected for example like this:

template <typename A>
size_t min_iterable_length(A&& a) {
  return std::size(a);
}

template <typename A, typename... Args>
size_t min_iterable_length(A&& head, Args&&... tail) {
  size_t n_t = min_iterable_length(std::forward<Args>(tail)...);
  return std::min(std::size(head), n_t);
}

template <typename F, typename... Args>
void for_each(F f, Args &&...args) {
  size_t length = min_iterable_length(std::forward<Args>(args)...);
  for (int i = 0; i < length; ++i) {
    f(args[i]...);
  }
}

or even:

template <typename A>
size_t min_iterable_length(const A& a) {
  return std::size(a);
}

template <typename A, typename... Args>
size_t min_iterable_length(const A& head, const Args&... tail) {
  size_t n_t = min_iterable_length(tail...);
  return std::min(std::size(head), n_t);
}

template <typename F, typename... Args>
void for_each(F f, Args &&...args) {
  size_t length = min_iterable_length(args...);
  for (int i = 0; i < length; ++i) {
    f(args[i]...);
  }
}

since you don't need forwarding inside min_iterable_length and it's perfectly fine to bind an lvalue reference to a function argument declared as rvalue or forwarding reference.

For conciseness, min_iterable_length can be removed altogether:

template <typename F, typename... Args>
void for_each(F f, Args&&... args) {
  size_t length = std::min({std::size(args)...});
  for (int i = 0; i < length; ++i) {
    f(args[i]...);
  }
}
maxplus
  • 502
  • 1
  • 12