Один метод под const& и &&

Тема в разделе "LANGS.C", создана пользователем _DEN_, 19 фев 2012.

  1. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    Привет.

    Есть класс, принимающий объект, и складирующий его во внутренний контейнер. Можно ли сделать так, чтобы прием объекта и по константной ссылке, и по rvalue-ссылке укладывался в один метод? Сейчас приходится делать так:

    Код (Text):
    1. template <class T>
    2. class some_container
    3. {
    4. public:
    5.  
    6.     void push(T const& val)
    7.     {
    8.         values.push_back(val);
    9.     }
    10.  
    11.     void push(T&& val)
    12.     {
    13.         values.push_back(val);
    14.     }
    15.  
    16. private:
    17.  
    18.     std::vector<T> values;
    19. };
    А хочется чтобы все укладывалось в один some_container::push.
     
  2. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    Код (Text):
    1. template <class T>
    2. class some_container
    3. {
    4. public:
    5.    template <class U>
    6.     void push(U val)
    7.     {
    8.         values.push_back(val);
    9.     }
    10. private:
    11.  
    12.     std::vector<T> values;
    13. };
     
  3. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers
    Хм, все это очень странно! MSVS 2010:

    Код (Text):
    1. template <class T>
    2. class some_container
    3. {
    4. public:
    5.  
    6.     template <class U>
    7.     void push(U val)
    8.     {
    9.         values.push_back(val); // Тут вызывается const&
    10.  
    11.         std::make_pair(val, val); // Тут вызывается &&
    12.     }
    13. private:
    14.  
    15.     std::vector<T> values;
    16. };
    17.  
    18. int main()
    19. {
    20.     some_container<std::string> sc;
    21.     sc.push(std::string("lalala"));
    22.     return 0;
    23. }
    И самое странное, что если заменить void push(U val) на void push(T&& val), то для vector::push_back всеравно вызывается const&, хотя реализация move-push_back в студийном STL есть, и интелисенс ее находит. Что я делаю не так?
     
  4. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    наверное
    Код (Text):
    1.    std::string str("lalala");
    2.     sc.push(std::move(str));
     
  5. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers

    Не, так тоже не хочет. Да и rvalue должно само сводиться к вызову &&, без move. Например здесь все ок, вызывается &&

    Код (Text):
    1. void nya(std::string const& s)
    2. {
    3. }
    4.  
    5. void nya(std::string&& s)
    6. {
    7. }
    8.  
    9. int main()
    10. {
    11.     nya(std::string("lalala"));
    12.     return 0;
    13. }
     
  6. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    а, похоже вот так надо

    Код (Text):
    1. template <class T>
    2. class some_container
    3. {
    4. public:
    5.    template <class U>
    6.     void push(U val)
    7.     {
    8.         values.push_back(std::forward<U>(val));
    9.     }
    10. private:
    11.  
    12.     std::vector<T> values;
    13. };
     
  7. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers

    Миша, все х##ня :)

    Делов в том, что U выводится в std::string (не ссылка, не rvalue, а просто std::string), и ее копия создается еще до вызова:

    sc.push(str); // Вот тут

    Далее-то да, происходит move, но смысла в нем нет, потому что копия уже создана. Поэтому до сих пор не ясно:

    1. Можно ли обойтись одним методом.
    2. Почему у вектора push_back && не вызывается, а в моем примере с nya && - вызывается? В чем разница?
     
  8. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    я же написал сверху, там вызывается push_back(T &&), как и в случае с nya
     
  9. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers
    А я тоже написал, что move происходит для копии, созданной в момент вызова, в связи с чем весь смысл move теряется. А в чем было дело, я, кажется, понял. Смотри что происходит:

    Код (Text):
    1. void mimimi(std::string const& s)
    2. {
    3. }
    4.  
    5. void mimimi(std::string&& s)
    6. {
    7. }
    8.  
    9. void nya(std::string const& s)
    10. {
    11.     mimimi(s);
    12. }
    13.  
    14. void nya(std::string&& s)
    15. {
    16.     mimimi(s); // mimimi const& !
    17. }
    18.  
    19. int main()
    20. {
    21.     mimimi(std::string("lalala")); // mimimi &&
    22.     nya(std::string("lalala")); // nya &&
    Как будто rvalue-ность видна только на самом первом этаже, и далее не передается.
     
  10. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    Я поэтому и написал std::forward

    http://ideone.com/cGbmT
    попробуй закомментировать move конструктор
     
  11. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers

    Так, привожу на твоем же примере. Заменяем main вот на это:

    Код (Text):
    1. int main(int argc, char* argv[])
    2. {
    3.     ccc c;
    4.     a a1;
    5.     c.push(a1);
    6.     return 0;
    7. }
    И видим в аутпуте:

    Код (Text):
    1. Create
    2. Copy
    3. Move
    4. Destroy
    5. Destroy
    6. Destroy
    Объект существовал в трех экземплярах. А должен был - в двух. Это произошло потому что U вывелся в std::string и в месте вызова push была создана копия объекта. move нужен для того чтобы избежать лишних копий, а тут мы наоборот получили +1 лишнюю. Если void push(U val) заменить на void push(U& val), то объект будет существовать в двух экземплярах, а copy-конструктор не будет вызван - то есть так, как и должно быть. В этом и есть мой изначальный вопрос - можно ли в одном вызове совместить move rvalue и передачу по const& lvalue. В твоем примере второе не работает - вместо передачи по ссылке происходит копирование объекта и его последующий move.
     
  12. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    Такая же херня происходит с std::make_pair, так что в случае с неанонимными объектами надо явно применять std::move.
    http://ideone.com/3TY3L
    Как это следует из стандарта я вычислять не готов. Видимо, это лучшее, на что способен move конструктор
     
  13. _DEN_

    _DEN_ DEN

    Публикаций:
    0
    Регистрация:
    8 окт 2003
    Сообщения:
    5.383
    Адрес:
    Йобастан
    sergegers
    Самое забавное вот что:
    Код (Text):
    1. void mimimi(std::string const& s)
    2. {
    3. }
    4.  
    5. void mimimi(std::string&& s)
    6. {
    7. }
    8.  
    9. void nya(std::string const& s)
    10. {
    11.     mimimi(s);
    12. }
    13.  
    14. void nya(std::string&& s)
    15. {
    16.     mimimi(s); // mimimi const&
    17.     std::make_pair(s, s); // make_pair &&, &&   <--- !!!
    18. }
    19.  
    20. int main()
    21. {
    22.     nya(std::string("lalala")); // nya &&
    23.     return 0;
    24. }
    Ща пока нет времени вникать, продолжу чуть позже. Вопрос с make_pair в моем случае пока что не ясен.
     
  14. sergegers

    sergegers New Member

    Публикаций:
    0
    Регистрация:
    8 июн 2008
    Сообщения:
    172
    В одном случае перегрузка функции, а в другом вывод типа аргумента шаблонной функции. Надо сравнивать правила.