Использование шаблона класса в шаблоне функции с универсальным эталонным параметром

Согласно msvc, gcc и clang, следующий код является незаконным:

template <typename T>
void f(T&& e) {
    std::vector<T> v;
    // do something with v and e ...
}
int main() {

    int i;
    f(i);
}

msvc дает

xmemory0 (591): ошибка C2528: "указатель": указатель на ссылку незаконное

gcc и clang дают похожие звуковые сообщения об ошибках. Обратите внимание, что универсальный опорный параметр e не используется. Компилятор, очевидно, не создает экземпляр вектора v, жалуясь на то, что он используется со ссылкой на int:

note: см. ссылку на экземпляр шаблона класса 'std::vector<T,std::allocator<_Ty>>' скомпилировано         с

    [
        T=int &,
        _Ty=int &
    ]

Но я не вижу, где создается шаблон функции f со ссылкой на int.

Может кто-нибудь объяснить ошибки компилятора, которые мы видим здесь?

Ответ 1

Когда f вызывается с lvalue int, T будет выводиться как int&, поэтому v будет std::vector<int&>. Это неверно.

Один из способов обойти это - удалить ссылки из T перед его использованием:

template <typename T>
void f(T&& e) {
    using value_type = typename std::remove_reference<T>::type;
    //using value_type = std::remove_reference_t<T>; for C++14
    std::vector<value_type> v;
    // do something with v and e ...
}

Однако, если вы хотите, чтобы функция была как можно более общей, вы должны использовать std::decay вместо std::remove_reference. Это позволит f работать с cv-квалифицированными типами, массивами и функциями.

Ответ 2

Скотт Майерс объясняет в этой статье .

Если выражение, инициализирующее универсальную ссылку, является значением l, универсальная ссылка становится ссылкой lvalue.

Если выражение инициализация универсальной ссылки - это rvalue, универсальная ссылка становится ссылкой rvalue.

В вашем случае i является lvalue и поэтому T выводится как int&.