Насколько я понимаю, приведенный вами раздел ответа точен, но вводит в заблуждение.
Во-первых, важно уточнить, что rvalue reference и forwarding reference - это не одно и то же, они просто имеют одинаковые обозначения &&
. Является ли это хорошей вещью, подлежит обсуждению.
template <typename T>
void foo(T&&); // deduced type == forwarding reference
void foo(int&&); // explicit type == rvalue reference
Достаточно просто. Так почему же следующая ссылка для пересылки ?
template <typename T>
void foo(const T&&); // const rvalue reference despite deduced type
Лучший ответ, который я могу вам дать, - «потому что». Похоже, это совершенно произвольное решение Комитета по стандартам. Я не вижу причин, по которым const T&&
не может быть ссылкой для пересылки ; это просто не потому, что так говорится в стандарте.
§14.8.2.1 / Вывод аргументов шаблона из вызова функции [temp.deduct.call]
Ссылка для пересылки - это rvalue-ссылка на cv-unqualified параметр шаблона.
Независимо от , почему это так, становится очевидным, что добавление cv-qualification является единственным способом сказать компилятору, что трактуемый тип следует рассматривать как ссылку на значение , а не как ссылка на пересылку . На что указывает ваша цитата из другого ответа.
Если const T && будет вести себя как пересылка ссылок, это сделает невозможным перегрузку функции шаблона, которая принимает в качестве параметра только ссылку на rvalue.
Причина, по которой я говорю, что это вводит в заблуждение, заключается в том, что если мы перегрузим шаблон для принятия const T&&
, тогда эта перегрузка будет предпочтительнее для всех значений rvalue независимо от cv -квалификация. Это не тот случай.
В следующем коде мы видим, что foo
принимает const rvalue ссылки , но ничего больше, потому что это не ссылка на пересылку .
struct Non_POD
{
Non_POD(int i) : m_i(i) { }
int m_i;
};
Non_POD foo() { return {0}; }
const Non_POD const_foo() { return {0}; }
template <typename T>
void bar(const T&& val)
{
std::cout << "Accepts: const rvalue ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
int main()
{
bar(foo());
bar(const_foo());
Non_POD x(0);
//bar(x); // error
}
Ожидаемый выход (GCC 7.1)
Принимает: const rvalue ref. Val является rvalue ссылкой.
Принимает: const rvalue ref. Val является rvalue ссылкой.
Кажется, это поддерживает кавычку, потому что она принимает ссылки на постоянные значения и преобразует ссылки на значения в ссылки на постоянные значения . Однако перегрузок не происходит. Если мы введем перегрузку, то увидим, что она принимает только const rvalue ссылки .
struct Non_POD
{
Non_POD(int i) : m_i(i) { }
int m_i;
};
Non_POD foo() { return {0}; }
const Non_POD const_foo() { return {0}; }
template <typename T>
void bar(const T&& val)
{
std::cout << "Accepts: const rvalue ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
template <typename T>
void bar(T&& val)
{
std::cout << "Accepts: forwarding ref. ";
if constexpr (std::is_rvalue_reference_v<decltype(val)>)
{
std::cout << "Val is rvalue reference.\n";
}
else if constexpr (std::is_lvalue_reference_v<decltype(val)>)
{
std::cout << "Val is lvalue reference.\n";
}
else
{
std::cout << "Val is lvalue.\n";
}
std::cout << std::endl;
}
int main()
{
Non_POD x(0);
const Non_POD cx(0);
bar(x);
bar(cx);
bar(Non_POD(0));
bar(foo());
bar(const_foo());
}
Ожидаемый результат (GCC 7.1)
Принимает: пересылка исх. Val - это значение lvalue.
Принимает: пересылка исх. Val - это значение lvalue.
Принимает: пересылка исх. Val является rvalue ссылкой.
Принимает: пересылка исх. Val является rvalue ссылкой.
Принимает: const rvalue ref. Val является rvalue ссылкой.
Из вышесказанного видно, что на самом деле нет способа объявить шаблон, который только принимает неконстантные ссылки .