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

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

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;
}

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

ошибка: «static void X :: xxx (T &&) [with T = int &]» не может быть перегружен
ошибка: с «static void X :: xxx (T &) [with 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;
}

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

ошибка: нет соответствующей функции для вызова «X :: xxx (int)»
примечание: кандидатами являются: static void X :: xxx (T &) [with 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;
}

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


person Sadeq    schedule 22.08.2010    source источник
comment
У меня болит голова. Так много амперсандов.   -  person James McNellis    schedule 22.08.2010
comment
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&& @ Джеймс &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&   -  person GManNickG    schedule 22.08.2010


Ответы (1)


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

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

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

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

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

Если мы попытаемся создать экземпляр 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&.

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

person James McNellis    schedule 22.08.2010
comment
Я примерно на 70% уверен, что вы полностью запомнили спецификации - person Michael Mrozek; 22.08.2010
comment
Спасибо за выражение значения TR. - person Sadeq; 22.08.2010
comment
Это правило заставляет std::forward работать? То есть это должен знать каждый программист на C ++ 0x ... :) - person UncleBens; 22.08.2010
comment
@UncleBens: это правило не применялось для ссылок на rvalue, оно восходит к тому времени, когда были ссылки на rvalue. В C ++ 98 попытка создать ссылку на ссылочный тип приводит к сбою определения типа (§14.8.2 / 2). Дефект 106 CWG и его устранение дает нам поведение, которое я описываю здесь (только для ссылок lvalue). Я думал, что это правило было включено в C ++ 03 (Вандевурде и Йосаттис в C ++ Templates ожидали, что это будет), но это не так. Однако это правило необходимо для работы forward. - person James McNellis; 22.08.2010
comment
Такое поведение называется свертыванием ссылок (я только что вспомнил об этом; весь день сводил меня с ума, что я не мог вспомнить, как это называлось). - person James McNellis; 23.08.2010