Вопрос

Рассмотрим ситуацию, когда шаблон функции должен пересылать аргумент, сохраняя при этом его lvalue, если это неконстантное lvalue, но сам по себе не зависит от того, чем на самом деле является аргумент, например:

template <typename T>
void target(T&) {
    cout << "non-const lvalue";
}

template <typename T>
void target(const T&) {
    cout << "const lvalue or rvalue";
}


template <typename T>
void forward(T& x) {
    target(x);
}

Когда x является значением r, а не T будучи выведенным к постоянному типу, он выдает ошибку:

int x = 0;
const int y = 0;

forward(x); // T = int
forward(y); // T = const int
forward(0); // Hopefully, T = const int, but actually an error
forward<const int>(0); // Works, T = const int

Кажется, что для forward для обработки rvalue (без вызова явных аргументов шаблона) должен быть forward(const T&) перегрузка, даже если его тело будет точной копией.

Есть ли способ избежать этого дублирования?

Это было полезно?

Решение

Это известная проблема и цель ссылок rvalue в C++0x.Проблема не имеет общего решения в C++03.

Есть какая-то архаичная историческая причина, почему это происходит, и она совершенно бессмысленна.Я помню, как однажды спросил, и ответ меня сильно расстроил.

Другие советы

В общем эта неприятная проблема с шаблонами должен требовать дублирования, поскольку семантика того, является ли переменная константной или нет, ссылкой или нет, совершенно различна.

Решением этой проблемы в C++11 является «decltype», но это плохая идея, поскольку все, что оно делает, — это составная и уже сломанная система типов.

Независимо от того, что говорит Стандарт или Комитет, const int не является и никогда не будет типом.И "int&" никогда не будет типом.Поэтому параметр типа в шаблоне никогда не должен быть привязан к таким нетипам, и, к счастью, это так и есть.К сожалению, вы все равно можете явно принудительно выполнить эту беспринципную замену.

Есть несколько идиотских правил, которые пытаются «исправить» эту проблему, например, «const const int» сводится к «const int». Я даже не уверен, что произойдет, если вы получите «int & &»:помните, что даже Стандарт не считает «int&» типом, существует тип «int lvalue», но он отличается:

int x;       // type is lvalue int
int &y = x;  // type is lvalue int

Правильное решение этой проблемы на самом деле довольно простое:все является изменяемым объектом.Выбросьте «const» (это не так уж полезно) и выбросьте ссылки, lvalue и rvalue.Совершенно очевидно, что все типы классов являются адресуемыми, независимо от того, имеют ли они значение rvalue (указатель «this» — это адрес).Комитет предпринял тщетную попытку запретить присвоение и адресацию значений.случай адресации работает, но его легко экранировать с помощью тривиального приведения.Случай присваивания вообще не работает (поскольку присваивание является функцией-членом, а значения r не являются константными, вы всегда можете присвоить классу типизированное значение rvalue).

В любом случае, в толпе метапрограммистов шаблонов есть «decltype», и с его помощью вы можете найти кодировку объявления, включая любые биты «const» и «&», а затем вы можете разложить эту кодировку с помощью различных библиотечных операторов.Раньше это было невозможно сделать, потому что эта информация на самом деле не является информацией о типе («ref» на самом деле является информацией о выделении памяти).

Когда x это rvalue

Но x никогда не является значением r, поскольку имена являются значениями lvalue.

Есть ли способ избежать этого дублирования?

В C++0x есть способ:

#include <utility>

template <typename T>
void forward(T&& x)   // note the two ampersands
{
    target(std::forward<T>(x));
}

Благодаря правилам свертывания ссылок выражение std::forward<T>(x) имеет ту же категорию значений, что и аргумент вашей собственной прямой функции.

Предполагая, что имеется k аргументов, насколько я понимаю, единственное «решение» в C++03 — это вручную записать 2^k функции пересылки, принимающие все возможные комбинации & и const& параметры.Для иллюстрации представьте себе, что target() на самом деле взял 2 параметра.Тогда вам понадобится:

template <typename T>
void forward2(T& x, T& y) {
    target(x, y);
}

template <typename T>
void forward2(T& x, T const& y) {
    target(x, y);
}

template <typename T>
void forward2(T const& x, T& y) {
    target(x, y);
}

template <typename T>
void forward2(T const& x, T const& y) {
    target(x, y);
}

Очевидно, что для больших k это становится очень громоздким, поэтому в C++0x есть ссылки на rvalue, как уже упоминалось в других ответах.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top