Вывод типа аргумента, ссылки и значения - PullRequest
6 голосов
/ 02 января 2011

Рассмотрим ситуацию, когда шаблон функции должен пересылать аргумент, сохраняя его lvalue-ness, в случае, если это неконстантное 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 является значением, вместо 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 для обработки rvalues ​​(без вызова явных аргументов шаблона) должна быть перегрузка forward(const T&), даже если ее тело будет точной копией.

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

Ответы [ 4 ]

5 голосов
/ 02 января 2011

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

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

2 голосов
/ 02 января 2011

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

Решение 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» (это не так уж полезно) и отбросьте ссылки, lvalues ​​и rvalues. Совершенно очевидно, что все типы классов являются адресуемыми, rvalue или нет (указатель «this» является адресом). Комитет предпринял тщетную попытку запретить присваивать и обращаться к значениям ... дело по делу работает, но его легко избежать с помощью тривиального броска. Регистр присваивания не работает вообще (поскольку присваивание является функцией-членом, а значения r не являются константными, вы всегда можете присвоить типу значение типа rvalue).

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

2 голосов
/ 02 января 2011

Когда x является r-значением

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

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

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

#include <utility>

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

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

0 голосов
/ 02 января 2011

Предполагая, что есть 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, поэтому rvalue ссылки в C ++ 0x, как упоминалось в других ответах.

...