Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?
Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра? RO> RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
Для вывода на экран достаточно только такого определения аргумента функции:
void foo(SomeType const& arg) {std::cout << arg;}
копирования не произойдёт, ведь такая нотация воспринимает и rvalue и lvalue ссылки. Писать две функции нет смысла (даже вредно).
Если в функции идёт присвоение, то надо ращеплять foo на две функции.
lvalue ссылки будут переданы через 1) foo функцию, здесь копирования не избежать (мы ведь хотим сохранить константность присваиваемого аргумента).
rvalue ссылки будут переданы через 2) foo функцию, константность присваиваемого здесь аргумента по определеию не нужна.
Здравствуйте, _smit, Вы писали:
_>Для вывода на экран достаточно только такого определения аргумента функции: _>void foo(SomeType const& arg) {std::cout << arg;}
Нет, речь о том, чтобы передать контейнер, отсортировать его — или копию — и потом в цикле вывести.
_>Если в функции идёт присвоение, то надо расщеплять foo на две функции.
_>1) void foo(const SomeType& arg) {SomeType tmp = arg;}
Чукча не читатель?
template <class SomeType> _>2) void foo(SomeType&& arg) {SomeType tmp = std::move<...>(arg);}
_>lvalue ссылки будут переданы через 1) foo функцию, здесь копирования не избежать (мы ведь хотим сохранить константность присваиваемого аргумента). _>rvalue ссылки будут переданы через 2) foo функцию, константность присваиваемого здесь аргумента по определеию не нужна.
Здравствуйте, Roman Odaisky, Вы писали:
RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?
Можно разжевать это для тех кто в танке?
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
1. Создать вектор с указателями на элементы контейнера
2. Отсортировать этот вектор по значениям, на которые указывают указатели
3. Вывести на экран
Не? Вроде как всё идеологически правильно
Здравствуйте, Roman Odaisky, Вы писали:
RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?
А в таком случае берем в руки напильник и прибегаем к испытанному дедовскому способу:
Здравствуйте, Roman Odaisky, Вы писали: RO>Здравствуйте, _smit, Вы писали: _>>void foo(const T& val) {std::cout << "foo(constT& val)=" << val << std::endl;} _>>void foo(T&& val) {std::cout << "foo(T&& val)=" << val << std::endl;} _>> const int i = 12;
RO>А без const вызовется foo(T &&), несмотря на то, что i — lvalue, и нельзя ее ломать.
Так всё правильно, я же не зря в комментарии в скобках указывал про константность аргумента. Если не константный -- используем перемещение. Про "и нельзя ее ломать" я не совсем понял. Вернее понимаю так, что передаваемый аргумент должен быть валиден после вызова функции? Ты про это:
8< =========== Результат: ============ >8
bar(T&& val):
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
8< ================================ >8
С перемещениями надо быть осторожным!!! Для этого и нужно внимательно следить за квалификатором const. Либо передавать по значению, как описывал Dave Abrahams. В своей статье он как раз и рассматривал вариант с сортировкой. Разве это не то, о чем спрашиваешь ты?
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
Честно сказать, последние мои сообщения прямого ответа на твой вопрос не дают, сейчас поиграюсь traits условиями ... с наскока не получается...
Здравствуйте, ArtDenis, Вы писали:
RO>>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра? AD>Можно разжевать это для тех кто в танке?
Проблема в том, что конструкция вида T&&, когда T — параметр шаблона — это уже не rvalue ссылка, а так называемая универсальная ссылка, которая может быть связана не только с rvalue но и с lvalue значениям в зависимости от типа фактического аргумента шаблонной функции. Во последнем случае тип фактического параметра будет описываться конструкцией T& &&, которая автоматически преобразуется в обычную ссылку: T& (так называемый reference collapsing).
Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений, это не получится сделать "в лоб" парой перегруженных шаблонных функций, а придется делать более хитрый маневр, например, реализацию шаблонной функции через шаблон класса, как показано здесь
Здравствуйте, Roman Odaisky, Вы писали:
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
задачу полностью не понял, однако могу предложить поступать оптимистично: сортировать с предположением, что элементы можно двигать и накидать везде std::move
если элементы действительно передвигаются, то никаких копий не будет, а если объекты не поддерживают перемещение, то по факту они скопируются, но ведь без копирования не получится.
это большое преимущество новых фишек: код один и поддерживает как копируемые объекты, так и перемещаемые
можно еще делать внешнюю сортировку, как уже ранее предложили, и забить на всякие передвигания
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
Здравствуйте, rg45, Вы писали: R>Здравствуйте, ArtDenis, Вы писали:
... R>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений, это не получится сделать "в лоб" парой перегруженных шаблонных функций, а придется делать более хитрый маневр, например, реализацию шаблонной функции через шаблон класса, как показано здесь
Здравствуйте, _smit, Вы писали:
_>Всё верно, однако можно разделить и через шаблонные функции:
_>
_>template<class T, class=typename std::enable_if<std::is_lvalue_reference<T&>::value>::type>
_>void fn(T& x)
_>{
_> std::cout << "lvalue! x=" << x << std::endl;
_>}
_>template<class T, class=typename std::enable_if<!std::is_lvalue_reference<T>::value>::type>
_>void fn(T&& x)
_>{
_> std::cout << "rvalue! x=" << x << std::endl;
_> //T y = move(x) // if need
_>}
_>
Выражение std::is_lvalue_reference<T&>::value всегда вычисляется в true для любых типов T. Поэтому первую перегрузку можно "облегчить", выбросив второй шаблонный параметр
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали: R>Здравствуйте, _smit, Вы писали: _>>Всё верно, однако можно разделить и через шаблонные функции:
... R>Выражение std::is_lvalue_reference<T&>::value всегда вычисляется в true для любых типов T. Поэтому первую перегрузку можно "облегчить", выбросив второй шаблонный параметр
да, согласен, я так и сделал в предыдущем сообщении, просто не акцентировал на этом
По большому счету здесь достаточно одной функции с передачей аргумента по lvalue ссылке. Оптимизацией и универсализацией можно заниматься долго с учетом специфики использования функции, ведь чем сложнее конструкция, тем легче нарваться на сайт эффект или допустить ошибку. + тесты.
R>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений
Если я верно понял задачу, разделение на lvalue и rvalue здесь бесполезно. Требования таковы, что объект можно модифицировать, если его передали как non-const rvalue, и нельзя модифицировать, если он приходит как lvalue или const rvalue — в этом случае надо создавать копию и работать с копией.
Здравствуйте, Ku-ku, Вы писали:
R>>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений
KK>Если я верно понял задачу, разделение на lvalue и rvalue здесь бесполезно. Требования таковы, что объект можно модифицировать, если его передали как non-const rvalue, и нельзя модифицировать, если он приходит как lvalue или const rvalue — в этом случае надо создавать копию и работать с копией.
На сколько я могу видеть, автору топика нужно отделить const T& от T&&, причем требуется чтобы T&& интерпретировалось как non-const rvalue reference и никак иначе. Но как бы то ни было, эти уточнения не меняют принципиально ни суть проблемы, ни предложенное мной решение. Суть проблемы: необходимо победить "всеядность" универсальной ссылки — это актуально в любой постановке. Ну и в предлагаемом мной решении любое уточнение задачи удовлетворяется написанием необходимых простых специализаций. (Не забываем, что специализации вовсе не обязаны дублировать реализацию задачи во всей ее полноте — они могут делегировать выполнение специальным функциям, а так же друг другу).
Один разок такую диспетчеризацию через специализации можно поюзать, но использовать этот приём многократно неудобно. Особенно для мемберов класса. ИМХО, лучше раз написать пару костылей
template <bool Condition, class T = void>
using enable_if = typename std::enable_if<Condition, T>::type;
template <class T>
struct is_modifiable_rval
: std::is_same<typename std::remove_const<typename std::remove_reference<T>::type>::type&&, T&&> {};
и дальше пользоваться сравнительно простой перегрузкой