Why is this an issue?
Type-constraints provide a terse way to express constraints on the type deduced for a given template parameter or auto placeholder. In a
situation when a type-constraint is applied to a forwarding reference parameter (T&&
), the corresponding concept will be checked
against the lvalue reference (if the argument is an lvalue) or the plain type (if the argument is an rvalue).
Even if it is possible to write a check that works for both plain types and references, it requires a dedicated effort, and a naive attempt may
silently fail for one or the other. For instance, a std::copyable
constraint is never satisfied for references, regardless of the
referenced type, while a std::copy_constructible
constraint always is.
This rule detects forwarding reference parameters that are constrained by the standard-provided concepts using type-constraint syntax.
Noncompliant code example
auto func(std::copy_constructible auto&& arg) // noncompliant
{ /* … */ }
template<std::copyable T>
auto func2(T&& arg) // noncompliant
{ /* … */ }
Compliant solution
auto func(auto&& argc)
requires std::copy_constructible<std::remove_cvref_t<decltype(arg)>>
{ /* … */ }
template<typename T>
requires std::copyable<std::remove_cvref_t<T>>
auto func2(T&& arg)
{ /* … */ }
Exceptions
The std::ranges::range
concept and its refinements (like std::ranges::forward_range
,
std::ranges::bidirectional_range
) are designed to handle forwarding references parameters, and will not raise issues for this rule.