Проблема с помощью ссылочной переменной параметра шаблона

Следующий небольшой пример показывает мою проблему:

template<class t=""> struct X
{
 static void xxx(T& x) { }
 static void xxx(T&& x) { }
};
int main(int argc, char** argv)
{
 int x = 9;
 X<int>::xxx(x); // OK.
 X<int&>::xxx(x); // ERROR!
 return 0;
}
</int&></int></class>

Сообщение об ошибке (GCC):

error: 'static void X:: xxx (T & &) [с T = int &] не может быть перегружен error: с помощью 'static void X:: xxx (T &) [с T = int &]

Почему? T = int& --- > Заменяется T& на int&& в static void xxx(T& x)?

Если ответ на вопрос да, то:

  • T& не является ссылкой lvalue и становится ссылкой rvalue!
  • И следующий код должен работать:

Но это не так:

template<class t=""> struct X
{
 static void xxx(T& x) { }
};
int main(int argc, char** argv)
{
 X<int&>::xxx(2); // ERROR!
 return 0;
}
</int&></class>

Сообщение об ошибке (GCC):

error: нет подходящей функции для вызова на "X:: xxx (int) примечание: кандидаты: static void X:: xxx (T &) [с T = int &]

Тогда T& с T = int& не равно T&& и не является ссылкой на rvalue. но если это не так, почему первый пример не работает? (это рекурсивная проблема!)

Но подобная проблема не возникала для типов указателей:

#include <iostream>
template<class t=""> struct X
{
 static void xxx(T* x) { std::cout << **x << std::endl; }
};
int main(int argc, char** argv)
{
 int x = 10;
 int* xx = &x;
 X<int*>::xxx(&xx); // OK. call X<int*>::xxx(int**)
 return 0;
}
</int*></int*></class></iostream>

Почему в этом поведении разные ссылки?

1 ответ

В стандарте языка С++ 11 есть объяснение того, как это работает в §8.3.2 [dcl.ref]/6 (переформатировано для удобочитаемости):

Если typedef, шаблон-шаблон типа или спецификатор decltype обозначают тип TR, который является ссылкой на тип T,

  • попытка создания типа "ссылка lvalue на cv TR" создает тип "lvalue reference to T"
  • попытка создать тип "rvalue reference to cv TR" создает тип TR.

Рассмотрим ваш пример (я переименовал ваш T как TR, чтобы он соответствовал указанному выше языку):

template<class tr=""> struct X
{
 static void xxx(TR& x) { }
 static void xxx(TR&& x) { }
};
</class>

Если мы попытаемся создать экземпляр X с TR = int& (так, T = int), то инстанцирования xxx будут следующими:

static void xxx(TR& x) { } --> static void xxx(int& x) { }
static void xxx(TR&& x) { } --> static void xxx(int& x) { }

В первом случае мы пытаемся создать ссылку "lvalue для TR", которая становится "ссылкой lvalue на T". T int, поэтому тип параметра становится int&.

Во втором случае мы пытаемся создать ссылку "rvalue для TR", которая становится TR, которая равна int&.

Тип параметра одинаков для обеих перегрузок, следовательно, ошибка.

licensed under cc by-sa 3.0 with attribution.