Я пытаюсь изучить концепцию типовых черт. И я написал код, чтобы проверить свое понимание:

#include <iostream>
#include <typeinfo>
#include <utility>

class Normal1 {};
class Normal2 {};
class Special {};

struct Normal_tag {};
struct Special_tag {};

template <typename T>
struct trait {
    typedef Normal_tag Type;
};

template <>
struct trait<Special> {
    typedef Special_tag Type;
};

template <typename T>
void handle_impl(T&& object, Normal_tag) {
    std::cout << "normal called\n";
}

template <typename T>
void handle_impl(T&& object, Special_tag) {
    std::cout << "special called\n";
}

// method 1: can't pass in rvalue
// template <typename T>
// void handle(T& object) {
//     handle_impl(object, typename trait<T>::Type());
//     std::cout << '\t' << typeid(T).name()                       << '\n'
//               << '\t' << typeid(typename trait<T>::Type).name() << '\n';
// }

// method 2: always lvalue
// template <typename T>
// void handle(const T& object) {
//     handle_impl(object, typename trait<T>::Type());
//     std::cout << '\t' << typeid(T).name()                       << '\n'
//               << '\t' << typeid(typename trait<T>::Type).name() << '\n';
// }

// method 3: try to use universal reference
template <typename T>
void handle(T&& object) {
    // handle_impl(object, typename trait<T>::Type());
    handle_impl(std::forward<T>(object), typename trait<T>::Type());
    std::cout << '\t' << typeid(T).name()                       << '\n'
              << '\t' << typeid(typename trait<T>::Type).name() << '\n';
}

int main(int argc, char *argv[])
{
    Normal1 n1;
    Normal2 n2;
    Special sp;

    handle(sp);                 // This line
    handle(n1);
    handle(n2);

    handle(Special());
    handle(Normal1());
    handle(Normal2());

    return 0;
}

Приведенный ниже результат - не то, что я ожидал, я хочу, чтобы специальный метод вызывался как для аргументов lvalue, так и для rvalue:

normal called
        7Special
        10Normal_tag
normal called
        7Normal1
        10Normal_tag
normal called
        7Normal2
        10Normal_tag
special called
        7Special
        11Special_tag
normal called
        7Normal1
        10Normal_tag
normal called
        7Normal2
        10Normal_tag

Я думаю, что вывод означает, что для создания экземпляра используется класс Special. Но почему я получаю Normal_tag? Почему вызов handle(sp); ведет себя так?

Я надеялся, что универсальная ссылка позаботится как об аргументе lvalue, так и rvalue, это плохой способ?

0
fengqi 9 Июн 2018 в 17:48

1 ответ

Лучший ответ

Когда вы вызываете handle(sp); sp имеет L-значение, поэтому в вашем шаблоне handle T выводится как Special&, но у вас нет специализация для Special&

template <>
struct trait<Special&> {
   typedef Special_tag Type;
};

Поэтому на выходе вы получили нормальный вызов .

3
rafix07 9 Июн 2018 в 15:02