html текст
All interests
  • All interests
  • Design
  • Food
  • Gadgets
  • Humor
  • News
  • Photo
  • Travel
  • Video
Click to see the next recommended page
Like it
Don't like
Add to Favorites

Return by value и const variables в C++11

Во многих языках программирования существует возможность объявлять объекты и переменные константными. И, соответственно, существуют рекомендации делать так, если Вы не собираетесь менять их значения. С приходом нового стандарта, в С++ появилась рекомендация возвращать объекты из функций по значению, потому что даже без RVO можно повысить производительность программы, за счет использования семантики перемещения. Что же будет, если использовать эти две рекомендации вместе: вернуть константный объект по значению? Попробуем разобраться далее.

Просматривая видеозаписи с недавно прошедшей конференции С++Now я наткнулся на один интересный момент (подводный камень). В конце одного из выступлений докладчик приводит следующий код:
struct s
{
    ...
};

s f()
{
    const s r;
    ...;
    return r;
}

и спрашивает, что произойдет с объектом r при возврате из функции?

Предположим, что у структуры s есть и конструктор копирования, и конструктор перемещения, а функция f содержит код, препятствующий применению RVO. Что же произойдет с объектом r при возврате из функции? Если объект создан без спецификатора const, то при возврате из функции он будет перемещен, а если создан с constскопирован. Подобное поведение замечено в GCC и в Clang (в Visual Studio не проверял). Что это: баг или ожидаемое поведение? Если объект сразу же будет уничтожен, почему не переместить его?

Со времен С++03 многие из нас привыкли к тому, что временные объекты и константы — это почти одно и тоже. И это логично, ведь поведение и тех, и других было схоже: они позволяли вызывать только функции, принимающие аргументы по значению или константной ссылке (или просто присваивать свои значения таким типам):
void f1(int a) { }
void f2(int& a) { }
void f3(const int& a) { }
int g() { return 0; }

int main()
{
    f1(g()); // OK
    f2(g()); // compile error
    f3(g()); // OK

    const int a = 0;
    f1(a); // OK
    f2(a); // compile error
    f3(a); // OK
}

В C++11 ситуация изменилась: теперь мы можем изменять временные объекты с помощью rvalue ссылок. Но не константы. Согласно стандарту, любое изменение объектов, объявленных со спецификатором const (будь то применение const_cast или извращений с указателями), приводит к неопределенному поведению (undefined behavior). Получается, что если бы компилятор сгенерировал код, использующий конструктор перемещения, то привел бы программу в состояние неопределенного поведения, что недопустимо. Иными словами, const-correctness стоит у компилятора в более высоком приоритете, чем подобная оптимизация. Все-таки С++ — это строго типизированный язык и любые неявные приведения ссылок и указателей от const типа к обычному — запрещены, например: const A a -> A&&.

Рассмотрим следующий код:
void f1(int& a) { }
void f2(const int& a) { }

int main()
{
    int a1 = 0;
    f1(a1); // OK

    int a2 = 0;
    f2(a2); // OK

    const int ca1 = 0;
    f1(ca1); // compile error

    const int ca2 = 0;
    f2(ca2); // OK
}

Стандарт С++ не позволяет неявно снимать квалификаторы с объектов. Если некоторый тип А можно привести как к A&, так и к const A&, то const A можно привести только к const A&. Теперь заменим lvalue ссылки на rvalue ссылки, добавив вызовы std::move при передачи параметров в функции… Именно! Мы не можем приводить const A a к A&&, но можем к const A&&. Если написать такой конструктор, то компилятор использует его для возврата константных переменных из функций, однако смысла в этом не больше, чем от обычного конструктора копирования. Поэтому такой тип ссылок обычно не используется.

Заключение

Как оказывается, некоторые рекомендуемые практики не сочетаются. В С++ постоянно приходится то тут, то там следить, как бы не запнуться об очередной подводный камень. Теперь нужно следить еще и за константами, чтобы они не усложнили Вам жизнь. Ведь никто не застрахован от такого кода (и даже если Вы рассчитываете на RVO и думаете, что данный случай Вам не важен, то можете потратить много времени на поиски причины, почему не компилируется этот код, если в структуре s нет копирующего конструктора):
struct s
{
    ...
};

s foo();

s bar()
{
    const auto r = foo();
    if (r.check_something())
        throw std::exception();
    return r;
}

И даже в этом коде есть копирование объекта (еще один подводный камень):
s bar()
{
    auto&& r = foo();
    ...;
    return r;
}

Предпочитайте такой вариант:
s bar()
{
    auto r = foo();
    ...;
    return r;
}

Или, только если Вы дорожите каждым конструктором перемещения:
s bar()
{
    auto&& r = foo();
    ...;
    return std::move(r);
}

Примечание

Не путайте предыдущий пример со следующим кодом, возвращающим rvalue ссылку:
s&& bar()
{
    auto&& r = foo();
    ...;
    return std::move(r);
}

Никогда так не делайте, здесь возвращается ссылка на уже уничтоженный объект.

И да пребудет с Вами Сила!
Читать дальше
Twitter
Одноклассники
Мой Мир

материал с habrahabr.ru

1

      Add

      You can create thematic collections and keep, for instance, all recipes in one place so you will never lose them.

      No images found
      Previous Next 0 / 0
      500
      • Advertisement
      • Animals
      • Architecture
      • Art
      • Auto
      • Aviation
      • Books
      • Cartoons
      • Celebrities
      • Children
      • Culture
      • Design
      • Economics
      • Education
      • Entertainment
      • Fashion
      • Fitness
      • Food
      • Gadgets
      • Games
      • Health
      • History
      • Hobby
      • Humor
      • Interior
      • Moto
      • Movies
      • Music
      • Nature
      • News
      • Photo
      • Pictures
      • Politics
      • Psychology
      • Science
      • Society
      • Sport
      • Technology
      • Travel
      • Video
      • Weapons
      • Web
      • Work
        Submit
        Valid formats are JPG, PNG, GIF.
        Not more than 5 Мb, please.
        30
        surfingbird.ru/site/
        RSS format guidelines
        500
        • Advertisement
        • Animals
        • Architecture
        • Art
        • Auto
        • Aviation
        • Books
        • Cartoons
        • Celebrities
        • Children
        • Culture
        • Design
        • Economics
        • Education
        • Entertainment
        • Fashion
        • Fitness
        • Food
        • Gadgets
        • Games
        • Health
        • History
        • Hobby
        • Humor
        • Interior
        • Moto
        • Movies
        • Music
        • Nature
        • News
        • Photo
        • Pictures
        • Politics
        • Psychology
        • Science
        • Society
        • Sport
        • Technology
        • Travel
        • Video
        • Weapons
        • Web
        • Work

          Submit

          Thank you! Wait for moderation.

          Тебе это не нравится?

          You can block the domain, tag, user or channel, and we'll stop recommend it to you. You can always unblock them in your settings.

          • habrahabr.ru
          • домен habrahabr.ru

          Get a link

          Спасибо, твоя жалоба принята.

          Log on to Surfingbird

          Recover
          Sign up

          or

          Welcome to Surfingbird.com!

          You'll find thousands of interesting pages, photos, and videos inside.
          Join!

          • Personal
            recommendations

          • Stash
            interesting and useful stuff

          • Anywhere,
            anytime

          Do we already know you? Login or restore the password.

          Close

          Add to collection

             

            Facebook

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

            Facebook

            К сожалению, вы не попадаете под условия акции