Привязка &&-аргументов к lvalue
От: Roman Odaisky Украина  
Дата: 28.08.13 20:20
Оценка:
struct X { };

                   void f(X const &) { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }
                   void f(X &&)      { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }

template <class T> void g(T const &) { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }
template <class T> void g(T &&)      { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }

int main()
{
    X x;
    f(x); // void f(const X&)
    g(x); // void g(T&&) [with T = X&]
}

Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?



Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?
До последнего не верил в пирамиду Лебедева.
Re: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 28.08.13 20:42
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
RO>
RO>struct X { };
RO>                   void f(X const &) { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }
RO>                   void f(X &&)      { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }
RO>template <class T> void g(T const &) { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }
RO>template <class T> void g(T &&)      { std::cout << BOOST_CURRENT_FUNCTION << std::endl; }

RO>int main()
RO>{
RO>    X x;
RO>    f(x); // void f(const X&)
RO>    g(x); // void g(T&&) [with T = X&]
RO>}
RO>

RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?
RO>

RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?

Для вывода на экран достаточно только такого определения аргумента функции:
void foo(SomeType const& arg) {std::cout << arg;}

копирования не произойдёт, ведь такая нотация воспринимает и rvalue и lvalue ссылки. Писать две функции нет смысла (даже вредно).

Если в функции идёт присвоение, то надо ращеплять foo на две функции.

1) void foo(const SomeType& arg) {SomeType tmp = arg;}

2) void foo(SomeType&& arg) {SomeType tmp = std::move<...>(arg);}

lvalue ссылки будут переданы через 1) foo функцию, здесь копирования не избежать (мы ведь хотим сохранить константность присваиваемого аргумента).
rvalue ссылки будут переданы через 2) foo функцию, константность присваиваемого здесь аргумента по определеию не нужна.
Re[2]: Привязка &&-аргументов к lvalue
От: Roman Odaisky Украина  
Дата: 28.08.13 20:52
Оценка:
Здравствуйте, _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 функцию, константность присваиваемого здесь аргумента по определеию не нужна.

...и lvalue-значения тоже пойдут в функцию foo-2!
До последнего не верил в пирамиду Лебедева.
Re[3]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 28.08.13 21:15
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
...
RO>...и lvalue-значения тоже пойдут в функцию foo-2!
template <typename T>
void foo(const T& val) {std::cout << "foo(constT& val)=" << val << std::endl;}

template <typename T>
void foo(T&& val) {std::cout << "foo(T&& val)=" << val << std::endl;}

int main() 
{
   const int i = 12;
   foo(i);
   foo(12);
   return 0;
}

output:
foo(constT& val)=12
foo(T&& val)=12

(MinGW+gcc4.6.3)

пардон, может где и невнимателен, у меня уже 4 часа утра... seep........
Re[4]: Привязка &&-аргументов к lvalue
От: Roman Odaisky Украина  
Дата: 28.08.13 21:55
Оценка:
Здравствуйте, _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;

А без const вызовется foo(T &&), несмотря на то, что i — lvalue, и нельзя ее ломать.
До последнего не верил в пирамиду Лебедева.
Re: Привязка &&-аргументов к lvalue
От: ArtDenis Россия  
Дата: 29.08.13 02:42
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?

Можно разжевать это для тех кто в танке?

RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?

1. Создать вектор с указателями на элементы контейнера
2. Отсортировать этот вектор по значениям, на которые указывают указатели
3. Вывести на экран
Не? Вроде как всё идеологически правильно
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re: Привязка &&-аргументов к lvalue
От: rg45 СССР  
Дата: 29.08.13 05:21
Оценка: 1 (1)
Здравствуйте, Roman Odaisky, Вы писали:

RO>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?


А в таком случае берем в руки напильник и прибегаем к испытанному дедовскому способу:

http://ideone.com/QnM8e0

#include <iostream>
#include <typeinfo>

struct X { };

template<typename T> 
struct aux
{
  void operator()(T&&) const { std::cout << 1 << std::endl; }  
};

template<typename T> 
struct aux<T&>
{
  void operator()(T&) const { std::cout << 2 << std::endl; }  
};

template<typename T> void g(T&& t)
{ 
    aux<T>()(std::forward<T>(t)); 
}

int main()
{
    X x;
    g(x);   // 2
    g(X()); // 1
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 29.08.13 06:13
Оценка:
Здравствуйте, 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, и нельзя ее ломать.


Так всё правильно, я же не зря в комментарии в скобках указывал про константность аргумента. Если не константный -- используем перемещение. Про "и нельзя ее ломать" я не совсем понял. Вернее понимаю так, что передаваемый аргумент должен быть валиден после вызова функции? Ты про это:

template <typename T>
void bar(const T& arg) 
{
   std::cout << "bar(constT& val):" << std::endl; 
   for(const auto& elem: arg) 
      std::cout << elem << ' ';
}

template <typename T>
void bar(T&& arg)
{
    typedef typename std::decay<decltype(arg)>::type _Td;
    std::cout << "bar(T&& val):" << std::endl;
    _Td temp = std::move(arg);  // does the "arg" after moving valid???
    for(const auto& elem: arg)
        std::cout << elem << ' ';

   // "temp" is local variable, will be destroyed (!)
}

void main()
{
   std::vector<int> my_array{3, 12};

   bar(my_array);

   std::cout << std::endl << "last=" << my_array.at(2) << std::endl; // <<<< BOMB CRASH!!!!!!1111
}

Да, так делать нельзя!!!

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. В своей статье он как раз и рассматривал вариант с сортировкой. Разве это не то, о чем спрашиваешь ты?
Re[6]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 29.08.13 06:31
Оценка:
Здравствуйте, _smit, Вы писали:

Небольшой патч к приведённому мною коду, а то несовсем наглядно корректно было.

template <typename T>
void bar(const T& arg)
{
   std::cout << "bar(constT& val):" << std::endl;
   for(const auto& elem: arg)
      std::cout << elem << ' ';
}

template <typename T>
void bar(T&& arg)
{
    typedef typename std::decay<decltype(arg)>::type _Td;
    std::cout << "bar(T&& val):" << std::endl;
    _Td temp = std::move(arg);  // does the "arg" after moving valid???
    for(const auto& elem: temp)
        std::cout << elem << ' ';

   // "temp" is local variable, will be destroyed (!)
}

int main() 
{
   std::vector<int> my_array{3, 12};
   bar(my_array);
   std::cout << std::endl << "last=" << my_array.at(1) << std::endl;

    return 0;
}
Re[7]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 29.08.13 08:11
Оценка:
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?

Честно сказать, последние мои сообщения прямого ответа на твой вопрос не дают, сейчас поиграюсь traits условиями ... с наскока не получается...
Re[2]: Привязка &&-аргументов к lvalue
От: rg45 СССР  
Дата: 29.08.13 08:16
Оценка: 6 (1) +1
Здравствуйте, ArtDenis, Вы писали:

RO>>Это что же значит, для шаблонных аргументов нельзя одной лишь перегрузкой выяснить, можно ли использовать аргумент для move-инициализации чего-нибудь? Надо еще анализировать тип фактического шаблонного параметра?

AD>Можно разжевать это для тех кто в танке?

Проблема в том, что конструкция вида T&&, когда T — параметр шаблона — это уже не rvalue ссылка, а так называемая универсальная ссылка, которая может быть связана не только с rvalue но и с lvalue значениям в зависимости от типа фактического аргумента шаблонной функции. Во последнем случае тип фактического параметра будет описываться конструкцией T& &&, которая автоматически преобразуется в обычную ссылку: T& (так называемый reference collapsing).

Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений, это не получится сделать "в лоб" парой перегруженных шаблонных функций, а придется делать более хитрый маневр, например, реализацию шаблонной функции через шаблон класса, как показано здесь
Автор: rg45
Дата: 29.08.13
. Прием старый, основанный на том факте, что частичная специализация не разрешена для шаблонных функций но разрешена для шаблонных классов.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: Привязка &&-аргументов к lvalue
От: uzhas Ниоткуда  
Дата: 29.08.13 08:19
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:

RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?

задачу полностью не понял, однако могу предложить поступать оптимистично: сортировать с предположением, что элементы можно двигать и накидать везде std::move
если элементы действительно передвигаются, то никаких копий не будет, а если объекты не поддерживают перемещение, то по факту они скопируются, но ведь без копирования не получится.
это большое преимущество новых фишек: код один и поддерживает как копируемые объекты, так и перемещаемые
можно еще делать внешнюю сортировку, как уже ранее предложили, и забить на всякие передвигания
Re: Привязка &&-аргументов к lvalue
От: Ku-ku  
Дата: 29.08.13 08:44
Оценка:
RO>Хочу, к примеру, шаблонную функцию, которая выведет на экран элементы любого контейнера в отсортированном виде. Как это сделать Идеологически Правильно, избегая лишних копирований?

Как вариант — http://ideone.com/6Y0ZAG
Re[3]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 29.08.13 09:06
Оценка: 4 (1)
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, ArtDenis, Вы писали:
...
R>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений, это не получится сделать "в лоб" парой перегруженных шаблонных функций, а придется делать более хитрый маневр, например, реализацию шаблонной функции через шаблон класса, как показано здесь
Автор: rg45
Дата: 29.08.13
. Прием старый, основанный на том факте, что частичная специализация не разрешена для шаблонных функций но разрешена для шаблонных классов.


Всё верно, однако можно разделить и через шаблонные функции:

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
}

void main()
{
   int x = 22;
   fn(x);
   fn("the string");
   fn(11);
}


но смущает результат вывода:

lvalue! x=22
lvalue! x=the string
rvalue! x=11

разве аргумент "the string" -- это не rvalue?
Re[4]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 29.08.13 09:16
Оценка:
Здравствуйте, _smit, Вы писали:
_>template<class T>
_>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
_>}
_>void main()
_>{
_>   int x = 22;
_>   fn(x);
_>   fn("the string");
_>   fn(11);
_>}
_>

_>разве аргумент "the string" -- это не rvalue?

вопрос снят, это "const char [11]&"
Re[4]: Привязка &&-аргументов к lvalue
От: rg45 СССР  
Дата: 30.08.13 05:40
Оценка: +1
Здравствуйте, _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. Поэтому первую перегрузку можно "облегчить", выбросив второй шаблонный параметр
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Привязка &&-аргументов к lvalue
От: _smit Россия  
Дата: 30.08.13 06:42
Оценка:
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, _smit, Вы писали:
_>>Всё верно, однако можно разделить и через шаблонные функции:
...
R>Выражение std::is_lvalue_reference<T&>::value всегда вычисляется в true для любых типов T. Поэтому первую перегрузку можно "облегчить", выбросив второй шаблонный параметр

да, согласен, я так и сделал в предыдущем сообщении, просто не акцентировал на этом
По большому счету здесь достаточно одной функции с передачей аргумента по lvalue ссылке. Оптимизацией и универсализацией можно заниматься долго с учетом специфики использования функции, ведь чем сложнее конструкция, тем легче нарваться на сайт эффект или допустить ошибку. + тесты.
Re[3]: Привязка &&-аргументов к lvalue
От: Ku-ku  
Дата: 30.08.13 17:03
Оценка:
R>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений

Если я верно понял задачу, разделение на lvalue и rvalue здесь бесполезно. Требования таковы, что объект можно модифицировать, если его передали как non-const rvalue, и нельзя модифицировать, если он приходит как lvalue или const rvalue — в этом случае надо создавать копию и работать с копией.
Re[4]: Привязка &&-аргументов к lvalue
От: rg45 СССР  
Дата: 30.08.13 18:48
Оценка:
Здравствуйте, Ku-ku, Вы писали:

R>>Таким образом, если потребуется предоставить различную реализацию для rvalue и lvalue значений


KK>Если я верно понял задачу, разделение на lvalue и rvalue здесь бесполезно. Требования таковы, что объект можно модифицировать, если его передали как non-const rvalue, и нельзя модифицировать, если он приходит как lvalue или const rvalue — в этом случае надо создавать копию и работать с копией.


На сколько я могу видеть, автору топика нужно отделить const T& от T&&, причем требуется чтобы T&& интерпретировалось как non-const rvalue reference и никак иначе. Но как бы то ни было, эти уточнения не меняют принципиально ни суть проблемы, ни предложенное мной решение. Суть проблемы: необходимо победить "всеядность" универсальной ссылки — это актуально в любой постановке. Ну и в предлагаемом мной решении любое уточнение задачи удовлетворяется написанием необходимых простых специализаций. (Не забываем, что специализации вовсе не обязаны дублировать реализацию задачи во всей ее полноте — они могут делегировать выполнение специальным функциям, а так же друг другу).

Простая иллюстрация сказанного:

http://ideone.com/U2ElNj

#include <iostream>
#include <typeinfo>

struct X { };

template<typename T> 
struct aux
{
  void operator()(T&&) const { std::cout << 1 << std::endl; }  
};

template<typename T> 
struct aux<const T>
{
  void operator()(const T&& t) const { aux<T&>()(t); }  
};

template<typename T> 
struct aux<T&>
{
  void operator()(const T&) const { std::cout << 2 << std::endl; }  
};

template<typename T> void g(T&& t)
{ 
    aux<T>()(std::forward<T>(t)); 
}

X foo() { return X(); }
const X bar() { return X(); }

int main()
{
    X x1;
    g(std::move(x1)); // 1
    g(foo());         // 1
    g(x1);            // 2
    g(bar());         // 2

    const X x2;
    g(std::move(x2)); // 2
    g(x2);            // 2
}
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: Привязка &&-аргументов к lvalue
От: Ku-ku  
Дата: 30.08.13 22:06
Оценка: 4 (1)
R>Ну и в предлагаемом мной решении любое уточнение задачи удовлетворяется написанием необходимых простых специализаций.

Полученный код явно превосходит по сложности

void foo(const std::string&);
void foo(std::string&&);

Один разок такую диспетчеризацию через специализации можно поюзать, но использовать этот приём многократно неудобно. Особенно для мемберов класса. ИМХО, лучше раз написать пару костылей

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&&> {};

и дальше пользоваться сравнительно простой перегрузкой

template <class T>
void fn(T const& x)
{
    std::cout << "not a modifiable temporary" << std::endl;
}
template <class T>
enable_if<is_modifiable_rval<T>{}> fn(T&& x)
{
    std::cout << "a modifiable temporary" << std::endl;
}

http://ideone.com/VoJ7sC
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.