Home > database >  Why universal reference as an input parameter doesn't work
Why universal reference as an input parameter doesn't work

Time:06-24

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.

  • Related