перегрузка для ref-qualifier get метода
От: B0FEE664  
Дата: 28.01.18 10:58
Оценка:
Какой тип следует возвращать методом 'get() &&;' ?

class Test
{
  public:
    const std::vector<int>& GetVector() &
    {
      return m_v;
    }

    const std::vector<int>& GetVector() const &
    {
      return m_v;
    }

    // std::vector<int> or std::vector<int>&& ?
    std::vector<int> GetVector() &&
    {
      return std::move(m_v);
    }

    const std::vector<int>& GetVector() const &&
    {
      return m_v;
    }
  private:
    std::vector<int> m_v;
};
И каждый день — без права на ошибку...
Отредактировано 28.01.2018 10:59 B0FEE664 . Предыдущая версия .
Re: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 28.01.18 11:04
Оценка: +1
Здравствуйте, B0FEE664, Вы писали:

BFE>Какой тип следует возвращать методом 'get() &&;' ?

BFE> std::vector<int> or std::vector<int>&& ?

Я бы выбрал std::vector<int>&&. А с этим возникают какие-то трудности?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: перегрузка для ref-qualifier get метода
От: B0FEE664  
Дата: 28.01.18 11:41
Оценка:
Здравствуйте, rg45, Вы писали:

R>Я бы выбрал std::vector<int>&&. А с этим возникают какие-то трудности?

Возвращение ссылки на член класса. Я не уверен, что так не будет неопределённого поведения...
И каждый день — без права на ошибку...
Re[3]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 28.01.18 11:48
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Возвращение ссылки на член класса. Я не уверен, что так не будет неопределённого поведения...


Ну если эта ссылка будет использована после прекращения времени жизни объекта, то конечно будет. Но это уже не имеет прямого отношения ни к move семантике, ни к rvalue ссылкам ведь ровно такая же проблема существует и со старыми добрыми lvalue ссылками. Я убежден, что это проблема вызымающего кода, а не разрабатываемого класса. Невозможно встроить в класс все возможные и невозможные защиты.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 28.01.18 11:54
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Какой тип следует возвращать методом 'get() &&;' ?


Возвращать, конечно, стоит только по значению (как и всегда в подобных случаях).
Более интересен вопрос, нужен ли в данном случае std::move.
Re[2]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 28.01.18 12:21
Оценка: +2
Здравствуйте, Constructor, Вы писали:

BFE>>Какой тип следует возвращать методом 'get() &&;' ?


C>Возвращать, конечно, стоит только по значению (как и всегда в подобных случаях).


Впервые слышу о таком правиле. Почему, например, std::optional::value не следует этому правилу?

ИМХО, ты упускаешь из виду, что если вызвающий код вызвал у rvalue объекта медод GetVector, то никаким другим способом он этот объект использовать уже не cможет. А если сможет, то либо это не rvalue объект, либо он использует несколько раз move для одного и того же объекта. Так или иначе, ошибиться случайно невозможно и никаких ошибок не возникнет в корректно написанном вызывающем коде.
--
Не можешь достичь желаемого — пожелай достигнутого.
Отредактировано 28.01.2018 13:01 rg45 . Предыдущая версия . Еще …
Отредактировано 28.01.2018 12:26 rg45 . Предыдущая версия .
Отредактировано 28.01.2018 12:24 rg45 . Предыдущая версия .
Отредактировано 28.01.2018 12:22 rg45 . Предыдущая версия .
Re[3]: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 28.01.18 13:29
Оценка: -1 :)
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, Constructor, Вы писали:


BFE>>>Какой тип следует возвращать методом 'get() &&;' ?


C>>Возвращать, конечно, стоит только по значению (как и всегда в подобных случаях).


R>Впервые слышу о таком правиле. Почему, например, std::optional::value не следует этому правилу?


Стандартная библиотека вряд ли может служить пособием по хорошему стилю написания C++-кода.

R>Так или иначе, ошибиться случайно невозможно и никаких ошибок не возникнет в корректно написанном вызывающем коде.


Следующий код корректно написан?

#include <iostream>


struct Bar
{
    Bar()
    {
        std::cout << "Bar::Bar(), x = " << x << std::endl;
    }
    
    ~Bar()
    {
        x = 0;
        std::cout << "Bar::~Bar(), x = " << x << std::endl;
    }
    
    void print_x() const
    {
        std::cout << "Bar::print_x(), x = " << x << std::endl;
    }
    
    int x{42};
};

struct Foo
{
    ~Foo()
    {
        std::cout << "Foo::~Foo()" << std::endl;
    }
    
    Bar&& get_bar() &&
    {
        return std::move(bar);
    }
    
    Bar bar;
};

int main()
{
    auto&& bar = Foo{}.get_bar();
    
    std::cout << "---------------------" << std::endl;
    bar.print_x();
}


Если поменять Bar&& get_bar() && на Bar get_bar() &&, то таких проблем не возникнет.
Re[4]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 28.01.18 13:49
Оценка: 1 (1) +1
Здравствуйте, Constructor, Вы писали:

C>Возвращать, конечно, стоит только по значению (как и всегда в подобных случаях).


R>>Впервые слышу о таком правиле. Почему, например, std::optional::value не следует этому правилу?


C>Стандартная библиотека вряд ли может служить пособием по хорошему стилю написания C++-кода.


Спорное утверждение. Стандартная библиотека описана в стандарте языка и является его неотъемлемой частью. А вот ссыылок на источники правила "всегда возвращать по значению" ты так и не предоставил.

R>>Так или иначе, ошибиться случайно невозможно и никаких ошибок не возникнет в корректно написанном вызывающем коде.


C>int main()

C>{
C> auto&& bar = Foo{}.get_bar();
C> std::cout << "---------------------" << std::endl;
C> bar.print_x();
C>}
C>[/ccode]
C>Следующий код корректно написан?

Нет конечно, этот код порождает битую ссылку, а потом ее использует. Это примерно то же самое, что и это:

  auto bar = std::srtring("Hello!").c_str();
  std::cout << bar << std::endl;


Только косяк здесь в клиентском коде, а вовсе не в библиотеке.

C>Если поменять Bar&& get_bar() && на Bar get_bar() &&, то таких проблем не возникнет.


И если поменять auto&& bar = ... на auto bar = ..., то тоже таких проблем не возникнет.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[5]: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 28.01.18 15:04
Оценка:
Здравствуйте, rg45, Вы писали:

C>>Стандартная библиотека вряд ли может служить пособием по хорошему стилю написания C++-кода.


R>Спорное утверждение. Стандартная библиотека описана в стандарте языка и является его неотъемлемой частью.


Стандартная библиотека является частью стандарта языка, безусловно. Каким образом это противоречит тому, что она "вряд ли может служить пособием по хорошему стилю написания C++-кода"?

R>А вот ссыылок на источники правила "всегда возвращать по значению" ты так и не предоставил.


О каком правиле идет речь? Я выразил свое мнение.
Но мне всегда казалось, что это достаточно общее место: возврат r-value ссылки (в особенности при передаче владения объектом, на который указывает ссылка) — это небезопасно и не рекомендуется (т.е. допустимо только в каких-то весьма и весьма частных случаях). Например, см. вопрос на StackOverflow. В данном случае ситуация дополнительно усугубляется тем, что мы точно знаем (благодаря ref-qualifier), что объект, который возвращается по ссылке, является частью другого объекта, который будет уничтожен в конце выражения.
Re[6]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 28.01.18 21:18
Оценка: +1
Здравствуйте, Constructor, Вы писали:

C>Стандартная библиотека является частью стандарта языка, безусловно. Каким образом это противоречит тому, что она "вряд ли может служить пособием по хорошему стилю написания C++-кода"?


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

C>О каком правиле идет речь? Я выразил свое мнение.


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

C>Но мне всегда казалось, что это достаточно общее место: возврат r-value ссылки (в особенности при передаче владения объектом, на который указывает ссылка) — это небезопасно и не рекомендуется (т.е. допустимо только в каких-то весьма и весьма частных случаях). Например, см. вопрос на StackOverflow. В данном случае ситуация дополнительно усугубляется тем, что мы точно знаем (благодаря ref-qualifier), что объект, который возвращается по ссылке, является частью другого объекта, который будет уничтожен в конце выражения.


Ну, до сих пор прозвучало единственное обоснование, почему нужно возвращать значение, а не ссылку — это то, что пользователь класса может написать так:

auto&& bar = Foo{}.get_bar();


Ну так пользователь может еще и не такое написать. Например, он може написать вот так:

const size_t size = Foo{}.get_bar().get_baz().get_cuux().size();


И вот здесь он уж точно удивится, нафига мы делаем столько никому ненужных копий тяжелых объектов.

Когда пользователь пишет конструкцию наподобие Foo{}.get_bar() он, вообще-то, должен понимать, ЧТО он вызывает, что получает и каково время жизни возникающих объектов. А если он этого не понимает, то как соломку ни стели, он все равно найдет способ выстрелить себе в ногу.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[7]: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 29.01.18 08:27
Оценка: 15 (3)
Здравствуйте, rg45, Вы писали:

C>>Стандартная библиотека является частью стандарта языка, безусловно. Каким образом это противоречит тому, что она "вряд ли может служить пособием по хорошему стилю написания C++-кода"?


R>Нравится тебе этот стиль или нет, но это наиболее широко используемая библиотека, с которой знаком любой разработчик С++. И, следуя этому стилю, ты, по крайней мере, не будешь нарушать принцип наименьшего удивления.


Какой "этот стиль"? Я говорил о "хорошем стиле" — best practices, которые существуют на данный момент в языке. И библиотека им зачастую не соответствует (иногда это происходит просто из-за того, что она не поспевает за изменениями в языке; иногда ради сохранения обратной совместимости; но бывают и ошибки, и просчеты, и недосмотры-недоделки, и чудачества).

C>>О каком правиле идет речь? Я выразил свое мнение.


R>Меня удивила категоричность, с которой ты высказал свое мнение. Прозвучало так, как будто это уже какая-то общепринятая практика.


Прошу прощения за (возможно неуместную) категоричность, но у меня просто глаза на лоб полезли, когда я увидел код, возвращающий r-value ссылку.

C>>Но мне всегда казалось, что это достаточно общее место: возврат r-value ссылки (в особенности при передаче владения объектом, на который указывает ссылка) — это небезопасно и не рекомендуется (т.е. допустимо только в каких-то весьма и весьма частных случаях). Например, см. вопрос на StackOverflow. В данном случае ситуация дополнительно усугубляется тем, что мы точно знаем (благодаря ref-qualifier), что объект, который возвращается по ссылке, является частью другого объекта, который будет уничтожен в конце выражения.


R>Ну, до сих пор прозвучало единственное обоснование, почему нужно возвращать значение, а не ссылку — это то, что пользователь класса может написать так:


R>
R>auto&& bar = Foo{}.get_bar();
R>


Вот еще одно обоснование, если прежнее не слишком нравится:

#include <iostream>
#include <vector>


struct Foo
{    
    ~Foo()
    {
        std::cout << "Foo::~Foo()" << std::endl;
        for (auto& e : v_)
        {
            e = 1;
        }
    }
    
    std::vector<int>&& get_vector() &&
    {
        return std::move(v_);
    }
    
    std::vector<int> v_;
};

int main()
{
    std::cout << "Foo.v_ elements:" << std::endl;
    for (const auto& e : Foo{{2, 3, 5, 8, 13}}.get_vector())
    {
        std::cout << e << ' ';
    }
    std::cout << std::endl;
}


R>Ну так пользователь может еще и не такое написать. Например, он може написать вот так:


R>
R>const size_t size = Foo{}.get_bar().get_baz().get_cuux().size();
R>


R>И вот здесь он уж точно удивится, нафига мы делаем столько никому ненужных копий тяжелых объектов.


О каких копиях идет речь? В данном случае будет вызываться цепочка конструкторов перемещения, относительно быстрых/эффективных по сравнению с конструкторами копирования.
Но такой код сам по себе вряд ли можно назвать "хорошим" (особенно если фигурирующие в нем объекты "тяжелые").

R>Когда пользователь пишет конструкцию наподобие Foo{}.get_bar() он, вообще-то, должен понимать, ЧТО он вызывает, что получает и каково время жизни возникающих объектов. А если он этого не понимает, то как соломку ни стели, он все равно найдет способ выстрелить себе в ногу.


В данном случае возможность написания сомнительного по стилю кода идет в комплекте с неиллюзорной опасностью внезапных отстрелов конечностей. Лично я склоняюсь к варианту написания потенциально более медленного кода без сопутствующей опции отстрела.
Re[8]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 29.01.18 09:19
Оценка:
Здравствуйте, Constructor, Вы писали:

C>Вот еще одно обоснование, если прежнее не слишком нравится:


Да, этот пример выглядит убедительнее.

R>>И вот здесь он уж точно удивится, нафига мы делаем столько никому ненужных копий тяжелых объектов.


C>О каких копиях идет речь? В данном случае будет вызываться цепочка конструкторов перемещения, относительно быстрых/эффективных по сравнению с конструкторами копирования.


Я начинаю склоняться в твою сторону. Беру таймаут "на подумать".
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[8]: перегрузка для ref-qualifier get метода
От: uzhas Ниоткуда  
Дата: 29.01.18 14:09
Оценка: 9 (1)
Здравствуйте, Constructor, Вы писали:

C>Прошу прощения за (возможно неуместную) категоричность, но у меня просто глаза на лоб полезли, когда я увидел код, возвращающий r-value ссылку.


твоё "глаза полезли" опять говорит о категоричности
сигнатуру "foo() &&" в первую очередь придумали для лучшего перформанса. как сайд-эффект получили ещё одну возможность отстрелить ногу
возвращать vector&& считаю допустимой практикой (и да, это реальная практика). люди пользуются успешно, несмотря на возможные проблемы при использовании
точно так же люди используют голые указатели. страшно и крешдампы возможны, зато быстро

с точки зрения теории и стандарта опять же никаких нарушений нет. можно возвращать и по значению и по &&

теперь по поводу std::move тебе вопрос: ты готов сделать return std::move или сделаешь копию члена класса при возвращении по значению? можно развернуто ответить на этот вопрос
спасибо
Отредактировано 29.01.2018 14:38 uzhas . Предыдущая версия .
Re: перегрузка для ref-qualifier get метода
От: N. I.  
Дата: 29.01.18 14:41
Оценка:
B0FEE664:

BFE>Какой тип следует возвращать методом 'get() &&;' ?


Полагаю, если было бы понятно, зачем такой get вообще нужен, подобных вопросов бы не возникало. Ну, а от нечего делать можно ещё подумать, что б этакое вернуть из

get() volatile &
get() volatile &&
get() const volatile &
get() const volatile &&

А то те скромные четыре метода get выглядят как-то несолидно, прям халтура какая-то. Вот если там с десяток вариаций get будет, то уже точно никто не посмеет усомниться в предусмотрительности автора. Кстати, там noexcept очень неплохо бы смотрелся.
Re[9]: перегрузка для ref-qualifier get метода
От: rg45 СССР  
Дата: 29.01.18 14:43
Оценка:
Здравствуйте, uzhas, Вы писали:

U>с точки зрения теории и стандарта опять же никаких нарушений нет. можно возвращать и по значению и по &&


Ну а что бы ты выбрал в качестве стандартного подхода?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: перегрузка для ref-qualifier get метода
От: uzhas Ниоткуда  
Дата: 29.01.18 14:44
Оценка: +1 :)
Здравствуйте, N. I., Вы писали:

NI>Полагаю, если было бы понятно, зачем такой get вообще нужен, подобных вопросов бы не возникало.

Друзь, давай ответ в студию
Re[9]: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 29.01.18 15:10
Оценка:
Здравствуйте, uzhas, Вы писали:

C>>Прошу прощения за (возможно неуместную) категоричность, но у меня просто глаза на лоб полезли, когда я увидел код, возвращающий r-value ссылку.


U>твоё "глаза полезли" опять говорит о категоричности


Грешен, каюсь.

U>с точки зрения теории и стандарта опять же никаких нарушений нет. можно возвращать и по значению и по &&


В этом не было никаких сомнений.

U>теперь по поводу std::move тебе вопрос: ты готов сделать return std::move или сделаешь копию члена класса при возвращении по значению? можно развернуто ответить на этот вопрос


К сожалению, по-видимому, никакой легальный elision в данном случае не сработает, поэтому необходимости в простом копировании нет. Это один из тех редких случаев, когда нужен именно return std::move(smth);. Или тут какой-то подвох есть?
Re[10]: перегрузка для ref-qualifier get метода
От: uzhas Ниоткуда  
Дата: 29.01.18 15:34
Оценка:
Здравствуйте, Constructor, Вы писали:

U>>с точки зрения теории и стандарта опять же никаких нарушений нет. можно возвращать и по значению и по &&

C>В этом не было никаких сомнений.
У ТС были сомнения: http://rsdn.org/forum/cpp/7035221.1
Автор: B0FEE664
Дата: 28.01.18


C>К сожалению, по-видимому, никакой легальный elision в данном случае не сработает, поэтому необходимости в простом копировании нет. Это один из тех редких случаев, когда нужен именно return std::move(smth);. Или тут какой-то подвох есть?


я думал в таком направлении: не может ли так случиться, что метод дернут дважды и во втором случае результат уже будет пустой (ну или valid, but unspecified state, для буквоедов) ?
Отредактировано 29.01.2018 15:35 uzhas . Предыдущая версия .
Re[3]: перегрузка для ref-qualifier get метода
От: N. I.  
Дата: 29.01.18 15:48
Оценка:
uzhas, Вы писали:

U>Друзь, давай ответ в студию


Если поставленная задача заключается в том, чтобы найти какое-нибудь применение для ref-qualifiers, то можно сделать типом возврата void и подождать, пока кто-нибудь не захочет использовать get с rvalue и не заценит юмор (есть вероятность того, что такое вообще никогда не случится).

Если использование ref-qualifiers диктуется реальными нуждами, то должны быть и вполне конкретные примеры вызова таких версий get, где уже будет более-менее видно, что оттуда выгодно возвращать.

Если нет никаких поводов связываться с ref-qualifiers, то и не надо там их использовать вовсе. Вот, как-то так.
Re[11]: перегрузка для ref-qualifier get метода
От: Constructor  
Дата: 29.01.18 15:51
Оценка:
Здравствуйте, uzhas, Вы писали:

C>>К сожалению, по-видимому, никакой легальный elision в данном случае не сработает, поэтому необходимости в простом копировании нет. Это один из тех редких случаев, когда нужен именно return std::move(smth);. Или тут какой-то подвох есть?


U>я думал в таком направлении: не может ли так случиться, что метод дернут дважды и во втором случае результат уже будет пустой (ну или valid, but unspecified state, для буквоедов) ?


Навскидку такое возможно только при прямом вредительстве, да и опасности особой тут не будет, т.к. состояние все-таки должно быть valid, хоть и unspecified.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.