Is_const не работает должным образом для ссылки

Я написал тестовую программу:

#include <iostream> 
#include <type_traits> 
using namespace std; 
template<class T> 
void f(T&& t) 
{ 
     cout<<is_const<T>()<<endl; 
     //++t; 
} 
int main() { 
     const int i=0; 
     f(i); 
     return 0; 
} 

Он выводит "0", показывая T не const! Это странно. Затем я изменил f:

template<class T> 
void f(T&& t) 
{ 
     cout<<is_const<T>()<<endl; 
     ++t; 
} 

Тогда возникает ошибка компилятора, говоря, что мы изменяем только чтение T. Так можно ли изменить T или нет? Есть ли какие-либо ошибочные предположения в моей программе?

Ответ 1

См. std:: is_const:

Если T - это тип, определенный константой (т.е. const или const volatile), обеспечивает постоянное значение члена равным true. Для любого другого типа значение равно false.

T объявляется как пересылка ссылок. Поэтому для вашего кода T будет выводиться как const int&, что является ссылкой. Ссылка не может быть const-defined, она не будет const. Точно так же нет ссылки на константу (т.е. int& const), потому что ссылка не может снова отскакиваться. const int& является ссылкой на const int; и заметим, что T, таким образом, не модифицируется.

Из стандарта $8.3.2/1 Ссылки [dcl.ref]

Cv-квалифицированные ссылки плохо сформированы, за исключением случаев, когда cv-квалификаторы вводятся с помощью typedef-name ([dcl.typedef], [temp.param]) или decltype-specifier ([dcl.type.simple]), и в этом случае cv-квалификаторы игнорируются.

Дополнительные примеры из cppreference:

std::cout << std::is_const<int>::value << '\n'; // false
std::cout << std::is_const<const int>::value  << '\n'; // true
std::cout << std::is_const<const int*>::value  << '\n'; // false
std::cout << std::is_const<int* const>::value  << '\n'; // true
std::cout << std::is_const<const int&>::value  << '\n'; // false

Ответ 2

Модифицирован ли t, зависит от типа t, который выводится на основе типа переданной переменной. В этом случае вы передаете const int, поэтому t имеет тип const int &, потому что вы принимаете его как ссылку пересылки.

Насколько почему is_const возвращает false, потому что t ссылочный тип и ссылки никогда не являются константами.

Ответ 3

Ваша функция шаблона (т.е. f) принимает в качестве параметра a справочную ссылку (универсальная ссылка a.k.a). Правила, определяющие вывод T, рассматриваются как правила обрушения ссылок. Эти правила приведены ниже:

  • T& & становится T&
  • T& && становится T&
  • T&& & становится T&
  • T&& && становится T&&

Теперь, согласно правилам сбрасывания ссылок, когда вы укажете параметр f int const i, T будет вычитаться до int const&.

В соответствии со стандартной таблицей С++ 52 is_const будет оцениваться до true, если T является const квалифицированным.

введите описание изображения здесь

Кроме того, в стандарте С++ §20.13.4.3/p5 Свойства типа [meta.unary.prop] показан следующий пример того, как работает черта типа is_const:

[Пример:

is_const<const volatile int>::value // true
is_const<const int*>::value // false
is_const<const int&>::value // false
is_const<int[3]>::value // false
is_const<const int[3]>::value // true

- конец примера]

Как вы можете видеть на третьей строке, которая наш случай is_const, оценивается как false. Зачем? Поскольку тип, переданный в is_const в качестве параметра шаблона, является ссылочным типом. Теперь ссылки по существу const в том смысле, что вы не можете изменить то, на что они ссылаются, но они не соответствуют const. Таким образом, is_const с ссылочным типом будет оцениваться до false.

Ответ 4

Ответ верхнего уровня заключается в том, что ссылки не соответствуют критериям cv. К ним могут относиться только те типы, на которые они ссылаются.

Но это один из тех случаев, когда имеет значение слово const. Когда вы вызываете:

template<class T> 
void f(T&& t) 

с l значением типа const int, что выводится T? Вы можете сказать const int& (что правильно), но похоже, что этот тип const (int&). Следовательно, возможная путаница в отношении T составляет const.

Но если вместо этого вы сказали, что он выведен как int const& (это тот же тип, что и раньше), здесь нет возможной путаницы, и, возможно, менее удивительно, почему std::is_const<int const&> есть std::false_type.

Ответ 5

Возможным решением может быть перехват типа const при перегрузке:

template<class T> 
void f(T&& t) 
{ 
     ++t; 
} 

template<class T> 
void f(const T& t) 
{ 
     std::cout << "const here" << std::endl;
} 

Затем ссылки на объекты const будут обрабатываться второй функцией.