vendredi 12 novembre 2021

Why can't lvalue references bind to const const "forwarding references"?

"forwarding references" is in quotes because const-qualified forwarding references aren't actually forwarding references, but I wanted to make it clear that I am specifically referring to function templates.

Take the following test functions (code is duplicated to avoid :

#include <iostream>
#include <type_traits>
#include <typeinfo>

using namespace std;

template <typename T, typename U> void print_types() {
  cout << (is_const_v<remove_reference_t<T>> ? "const " : "")
       << typeid(T).name()
       << (is_rvalue_reference_v<T>   ? " &&"
           : is_lvalue_reference_v<T> ? " &"
                                      : "")
       << ", " << (is_const_v<remove_reference_t<U>> ? "const " : "")
       << typeid(U).name()
       << (is_rvalue_reference_v<U>   ? " &&"
           : is_lvalue_reference_v<U> ? " &"
                                      : "")
       << endl;
}

template <typename T> void print_rvalue_reference(T &&t) {
  print_types<T, decltype(t)>();
}

template <typename T> void print_const_rvalue_reference(const T &&t) {
  print_types<T, decltype(t)>();
}

int main() {
  int i = 1;
  const int j = 1;

  print_rvalue_reference(1); // int, int &&
  print_rvalue_reference(i); // int &, int &
  print_rvalue_reference(j); // const int &, const int &

  print_const_rvalue_reference(1); // int, const int &&
  print_const_rvalue_reference(i); // error
  print_const_rvalue_reference(j); // error
}

First, I wanted to note that print_rvalue_reference(j) only works because print_rvalue_reference never uses t in a non-const context. If this were not the case, the template instantiation would fail.

I am confused why the last two calls in main cause errors during compilation. Reference collapsing allows using T = int &; const T && to become int & and using T = const int &; const T && to become const int &, which means print_const_rvalue_reference<int &>(i) and print_const_rvalue_reference<const int &>(j) are valid.

Why does print_const_rvalue_reference(j) deduce T to be int instead of const int & when print_rvalue_reference(j) does deduce T to be const int &? Are (true) forwarding references special-cased to yield int & instead of int as part of its deduction guide?

Aucun commentaire:

Enregistrer un commentaire