C ++ - поведение специализации шаблона функции со ссылкой на значение - PullRequest
1 голос
/ 11 февраля 2011

Я пытаюсь реализовать функцию разыменования условного указателя. Основная идея заключается в следующем:

return is_pointer(arg) ? *arg : arg

Чтобы ограничить количество необходимых специализаций, я пытаюсь использовать rvalue ссылки для случая, когда arg не указатель. Вот моя текущая реализация (std::cout существует исключительно для целей отладки):

template< typename T >
inline typename std::enable_if< std::is_pointer< T >::value == false, T >::type deref(T&& t)
{
    std::cout << std::is_pointer< T >::value << std::endl;
    std::cout << typeid (T).name() << std::endl;
    return t;
}

template< typename T >
inline typename std::enable_if< std::is_pointer< T >::value == true, typename std::remove_pointer< T >::type& >::type deref(T t)
{
    std::cout << std::is_pointer< T >::value << std::endl;
    std::cout << typeid (T).name() << std::endl;
    return *t;
}

Теперь я получаю довольно странное поведение в GCC 4.6. Первая перегрузка используется как для типов без указателей, так и для типов указателей. Очевидно, что при использовании типа указателя он конфликтует со второй перегрузкой. Если я закомментирую второй и вызову следующее, используя первый ...

int q;
int *p = &q;
deref(p);

... соответствующий вывод консоли:

0
Pi

Как возможно, что тип без указателя (согласно std::is_pointer) также является типом указателя (согласно typeid) в том же контексте? Конфликт возникает между обеими перегрузками из-за std::is_pointer, неправильно сообщающего p как тип без указателя. Кроме того, когда я заменяю ссылку r-значения стандартной ссылкой в ​​первой перегрузке:

inline typename std::enable_if< std::is_pointer< T >::value == false, T >::type deref(T& t)

Это больше не конфликтует со второй перегрузкой ... Я просто не понимаю, что происходит. Кстати, использование второй перегрузки дает (как и следовало ожидать):

1
Pi

Спасибо за вашу помощь.

Ответы [ 2 ]

3 голосов
/ 12 февраля 2011

Насколько я понимаю

template <T>
void foo(T&&)

означает, что если T является lvalue, оно будет выведено как ссылка (T = int*& в вашем случае), а после свертывания ссылки int*&&& будет получена обычная ссылка lvalue int*&. Если бы это не было так, этот синтаксис захватил бы что-нибудь как ссылку на значение. Принимая во внимание, что смысл состоит в том, чтобы связать lvalue со ссылками lvalue и rvalues ​​со ссылками rvalue.

И is_pointer<int*&> не соответствует действительности. Следовательно, вы можете попробовать применить remove_reference<T>.

2 голосов
/ 11 февраля 2011

В вашей первой перегрузке T выводится как int * &. Попробуйте в первой перегрузке использовать remove_reference<T>::type в своем enable_if-тестировании и выводе.

...