I have a template class and within that class I have a static template function with universal/forwarding reference parameters. The idea is to perfect forward arguments to the function.
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template <typename T>
struct FooClass<T> {
inline static void impl(T&& b) {
if constexpr (is_lvreference<T>::value)
std::cout << "T&" << std::endl;
else if constexpr (is_lreference_const<T>::value)
std::cout << "const T&" << std::endl;
else if constexpr (std::is_rvalue_reference<T>::value)
std::cout << "T&&" << std::endl;
else
std::cout << "T" << std::endl;
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
This all works fine and the output is as expected:
const T&
T&&
T&
However, if I was to change the foo()
function like this:
template <class... T>
inline void foo(T&&... args) {
FooClass<T...>::impl(std::forward<T>(args)...);
}
Notice that the FooClass
class doesn't get a forwarding reference. Then the output is:
const T&
T
T&
The value category for the rvalue reference is not passed to the function impl()
even though I'm using std::forward
. Why does this happen? Is it because I'm not calling impl()
in a deducible context as T has already been deduced for FooClass
? How to avoid such an issue?
(This question is related to another one posted earlier today).
Edit
I changed FooClass<T>::impl()
to be a template function. Then I found that else if constexpr (std::is_rvalue_reference<T>::value)
was never true. I discovered ths was because rvalues are deduced to be of type T
and not T&&
. So I added some print functions and found that perfect forwarding now worked:
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template<typename T>
void printme(const T&) {
std::cout << "constant lvalue reference" << std::endl;
}
template<typename T>
void printme(T&) {
std::cout << "lvalue reference" << std::endl;
}
template<typename T>
void printme(T&&) {
std::cout << "rvalue reference" << std::endl;
}
template <typename T>
struct FooClass<T> {
template <typename Arg>
inline static void impl(Arg&& b) {
printme(std::forward<Arg>(b));
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
Output:
constant lvalue reference
rvalue reference
lvalue reference