template<typename T>
constexpr auto log_value(T&& value) {
if constexpr (std::is_enum_v<T>) {
cout << "enum" << endl;
}
else {
cout << "normal" << endl;
}
}
I have a function to judge whether some value is an enum, and I test it by
enum class A {
a,
s,
d
};
int main() {
auto s = A::a;
const auto& s1 = s;
auto&& s2 = A::a;
log_value(s);
log_value(s1);
log_value(s2);
log_value(A::a);
But the result is
normal
normal
normal
enum
When I change it into:
template<typename T>
constexpr auto log_value_const_left(const T& value) {
if constexpr (std::is_enum_v<T>) {
cout << "enum" << endl;
}
else {
cout << "normal" << endl;
}
}
It works
enum
enum
enum
enum
Now I just wonder why log_value
doesn't work? Isn't the input parameter T&& value
in log_value
called universal reference which could refer to either lvalue reference or rvalue reference?
And if std::is_enum_v<T> != true
in log_value
,what is it exactly? Why it's changed anyway?
CodePudding user response:
The way forwarding (aka universal) references work for lvalues is by deducing the referenced type (T
in your case) to an lvalue reference (A &
in your case). Then T &&
also becomes A &
according to the reference collapsing rules (&
&&
= &
).
For rvalues this is not needed, and T
is deduced to a non-reference.
You want std::is_enum_v<std::remove_reference_t<T>>
.
CodePudding user response:
you could use your first version if you use std::forward.
enum class A {
a,
s,
d
};
int main() {
auto s = A::a;
const auto& s1 = s;
auto&& s2 = A::a;
log_value(std::move(s));
log_value(std::move(s1));
log_value(std::move(s2));
log_value(A::a);
}
But std::remove_reference_t or in c 20 std::remove_cvref is your way to go.