Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

auto&& versus constrained auto&& in C++ templates

MSVC /std:c++20 reports the error shown below for constrained auto&&

#include<concepts>

class Base { public: Base() {} };
class Derived : public Base { public: Derived() {} };

template<typename T> concept con = std::is_class_v<T>
&& std::derived_from<T, Base>;
void f(con auto&& x) {}

int main()
{
   f(Derived()); // r value: Ok

   Derived d;
   f(d); // l-value: no matching overloaded function found
}

Remove "con" from "con auto&&" and the above code compiles with no error. I would like to understand why adding a constraint to an auto&& template parameter changes it from a "universal reference" to an R-value only reference. Does this behavior conform to the C++20 standard or is it something unique to MSVC?

Searched C++ forums on the internet and found nothing explaining this specific language use case. However there may be something that I missed or otherwise did not notice.

like image 333
Brian939 Avatar asked Oct 16 '25 04:10

Brian939


2 Answers

Because on the instantiation with Derived& you'll be checking

std::derived_from<Derived&, Base>
//              this guy ^          

To make your concept immune to varying value categories, you can use std::decay_t:

template<typename T> 
concept con = 
    std::is_class_v<std::decay_t<T>> && 
    std::derived_from<std::decay_t<T>, Base>;

so purely the types are checked w/o their value categories

Demo

like image 159
Nikos Athanasiou Avatar answered Oct 17 '25 17:10

Nikos Athanasiou


I would like to understand why adding a constraint to an auto&& template parameter changes it from a "universal reference" to an R-value only reference.

It did not. The function parameter here is still a forwarding reference.

However, your constraint is requiring that the deduced type is (a) a class type (b) that is derived from Base. In the call f(d), the template parameter deduces as Derived&, which is not a class type (nor is it derived from Base), so you've effectively made your function template only accept rvalues.

What you're looking for is

template <class T>
    requires std::derived_from<std::remove_cvref_t<T>, Base>
void f(T&& x);

Or to put that remove_cvref_t in the concept definition.

Note that the is_class check is redundant. Only class types can derive from another type.

like image 20
Barry Avatar answered Oct 17 '25 18:10

Barry