Здравствуйте, Went, Вы писали:
W>1. Почему тип T выводится как int, а не как int&? W>2. Чем руководствовались разработчики стандарта? W>3. Как это обойти?
W>Спасибо.
Просматривая бегло стандарт, я нашел к примеру в разделе "14.8.2.3 Deducing conversion function template arguments" следующее предложение:
"If A is a reference type, the type referred to by A is used for type deduction."
Если внимательно просмотреть стандарт, я думаю, можно найти аналогичное утверждение и для других случаев вывода параметров шаблона.
W>1. Почему тип T выводится как int, а не как int&?
Ответили. W>2. Чем руководствовались разработчики стандарта?
А как иначе? Для ссылок выводить аргумент как ссылку? Это совершенно не обязательно то, чего человек хочет. W>3. Как это обойти?
Просто:
Здравствуйте, Went, Вы писали:
W>Здравствуйте. Вопрос, наверное, ламерский, но меня такое очень удивило. W>Итак, такой код: W>
W>template<typename T>
W>void f(T x)
W>{
W>}
W>void main()
W>{
W> int i = 0;
W> int& ri = i;
W> f(ri);
W>}
W>
W>1. Почему тип T выводится как int, а не как int&?
Тебя же не удивляет, что T выводится как int при передаче в эту функцию i вместо ri? А между тем, выражения i и ri имеют одинаковый тип — lvalue of int, а значит и результат выведения типов для них одинаков.
Почему сделали так, а не иначе? Ну, вероятно, не захотели брать на себя ответственность за сюрпризы, которые могут случаться при подобных сценариях:
template<typename T>
void f(T x)
{
x = foo(); //T is reference? An outer variable is modified!
}
Если нужно, все-таки, чтобы параметр передавался по ссылке, ты этого можешь добиться различными способами: явно указать тип шаблонного параметра при вызове функции, использовать ссылку при объявлении формального параметра шаблонной функции, определить несколько перегрузок одноименной шаблонной функции, и т.д. в зависимости от задачи. Главное, что при этом ты никому не подложишь "сюрпризов".
--
Справедливость выше закона. А человечность выше справедливости.
Здравствуйте, Went, Вы писали:
W>Здравствуйте, rg45, Вы писали: R>> определить несколько перегрузок одноименной шаблонной функции W>Да, это то, что надо. Спасибо!
Но различать i и ri таким способом ты всё равно не сможешь
Здравствуйте, Кодт, Вы писали:
К>Но различать i и ri таким способом ты всё равно не сможешь
Авв. Действительно. Если перегрузить и для значения, и для ссылки, идет неоднозначность вызова
К>Лучше расскажи, что за задачу ты решаешь.
Есть тип Variant. Может хранить объект любого типа, хоть ссылку, хоть указатель, хоть сам объект. Хотелось бы такого:
int x;
int& rx = x;
Variant v;
v.set(x); // v хранит сам объект
v.set(rx); // v должен хранить ссылку на объект (внутри реализовано через указатель, но это детали), но он не отличает это от верхней строчки
Если я пишу явно
v.set<int&>(rx);
то все работает как надо.
Я не уверен, что мне обязательно нужно без явного указания. Но хочется все-таки такую возможность иметь.
W>int x;
W>int& rx = x;
W>Variant v;
W>v.set(x); // v хранит сам объект
W>v.set(rx); // v должен хранить ссылку на объект (внутри реализовано через указатель, но это детали), но он не отличает это от верхней строчки
Это странное и нереализуемое желание. Шаблон функции никак не может определить, что ему передали аргументом — ссылку или не ссылку.
К>>Лучше расскажи, что за задачу ты решаешь. W>Есть тип Variant. Может хранить объект любого типа, хоть ссылку, хоть указатель, хоть сам объект. Хотелось бы такого: W>
W>int x;
W>int& rx = x;
W>Variant v;
W>v.set(x); // v хранит сам объект
W>v.set(rx); // v должен хранить ссылку на объект (внутри реализовано через указатель, но это детали), но он не отличает это от верхней строчки
W>
W>Если я пишу явно W>
W>v.set<int&>(rx);
W>
W>то все работает как надо.
W>Я не уверен, что мне обязательно нужно без явного указания. Но хочется все-таки такую возможность иметь.
ИМХО, это правильно, что такой возможности нет. Она чревата неожиданностями.
К примеру, как по-твоему должны бы отработать следующие случаи:
Здравствуйте, Went, Вы писали:
W>Есть тип Variant. Может хранить объект любого типа, хоть ссылку, хоть указатель, хоть сам объект. Хотелось бы такого: W>
W>int x;
W>int& rx = x;
W>Variant v;
W>v.set(x); // v хранит сам объект
W>v.set(rx); // v должен хранить ссылку на объект (внутри реализовано через указатель, но это детали), но он не отличает это от верхней строчки
W>
W>Если я пишу явно W>
W>v.set<int&>(rx);
W>
W>то все работает как надо.
W>Я не уверен, что мне обязательно нужно без явного указания. Но хочется все-таки такую возможность иметь.
decltype(x) и decltype(rx) будут разными типами, int и int&, соответственно. Можно этим воспользоваться, обернув всё в макрос, чтобы избежать повтора.
Как-то так:
W>Я не уверен, что мне обязательно нужно без явного указания. Но хочется все-таки такую возможность иметь.
Так ты можешь и просто int засунуть, как ссылку
По идее это должно быть два разных метода.
Типа Set и SetRef
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Went, Вы писали:
W>Есть тип Variant. Может хранить объект любого типа, хоть ссылку, хоть указатель, хоть сам объект.
<> W>Я не уверен, что мне обязательно нужно без явного указания. Но хочется все-таки такую возможность иметь.
Лучше, всё же, делать явные указания.
Потому что
1) компилятор не может отличить (или, по крайней мере, сообщить это тебе) характер ссылки — на временный объект, на локальный объект, на внешний...
2) иногда нужно передать значение, получаемое из lvalue (например, разыменовав указатель); не делать же промежуточную rvalue-копию ради этого.
3) опять же, константность-неконстантность
Возможно, что достаточно просто отказаться от хранения в варианте ссылок. Только значения и указатели (которые — тоже значения).
Если нужно ввести третью категорию данных — заведомо ненулевые указатели, то посмотри в сторону boost/std(C++11) :: ref, cref.
Это избавит от необходимости в методах Set/SetRef/SetCRef.
Также возможно, что нет нужды изобретать велосипед: boost::any — вариант, который может хранить любые значения.
Здравствуйте, Кодт, Вы писали: К>Также возможно, что нет нужды изобретать велосипед: boost::any — вариант, который может хранить любые значения.
Основная фишка моего Variant, это возможность бинарной сериализации, которую boost::any по своей природе не тянет, ИМХО. Ну, там хранится буфер данных и указатель в таблице дескрипторов типа. Хранить ссылки, конечно, это экзотика, но хочется полного охвата.
Здравствуйте, Went, Вы писали:
W>Здравствуйте, Кодт, Вы писали: К>>Также возможно, что нет нужды изобретать велосипед: boost::any — вариант, который может хранить любые значения. W>Основная фишка моего Variant, это возможность бинарной сериализации, которую boost::any по своей природе не тянет, ИМХО. Ну, там хранится буфер данных и указатель в таблице дескрипторов типа. Хранить ссылки, конечно, это экзотика, но хочется полного охвата.
не есть еще вариант с boost::ref() (и boost::reference_wrapper) или, если у тебя C++11, std::ref()/std::cref() с тем же std::reference_wrapper.
для враппера можно сделать специализацию и тут уже будет явно понятно что вызывающий хочет именно ссылку...