Исключение копирования / перемещения при выводе членов из временного - PullRequest
0 голосов
/ 13 сентября 2018

Я бы хотел вывезти членов временного персонала без ненужного перемещения или копирования.

Предположим, у меня есть:

class TP {
    T _t1, _t2;
};

Я бы хотел получить _t1 и _t2 от TP(). Возможно ли это без копирования / перемещения участников?

Я пытался с кортежами и пытался «переслать» (я не думаю, что это возможно) участников, но лучшее, что я мог получить, это движение или члены, умирающие немедленно.

На следующей игровой площадке использование B::as_tuple2 приводит к тому, что участники умирают слишком рано, если только результат не привязан к типу не-ref, тогда члены перемещаются. B::as_tuple просто перемещается безопасно с auto на стороне клиента.

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

Можно ли продлить / передать жизнь элемента на внешнюю переменную или исключить перемещение / копирование? Мне это нужно с версией c ++ 14, но я не смог заставить ее работать и на c ++ 17, поэтому мне интересны оба варианта.

Детская площадка

#include <tuple>
#include <iostream>

using std::cout;
class Shawty {
/**
 * Pronounced shouty.
 **/
    public:
    Shawty() : _id(Shawty::id++) {cout << _id << " ctor\n"; }
    Shawty(Shawty && s) : _id(Shawty::id++) { cout << _id << " moved from " << s._id << "\n"; }
    Shawty(const Shawty & s) : _id(Shawty::id++) { cout << _id << " copied from " << s._id << "\n"; }
    Shawty& operator=(Shawty && s) { cout << _id << " =moved from " << s._id << "\n"; return *this;}
    Shawty& operator=(Shawty & s) { cout << _id << " =copied from " << s._id << "\n"; return *this;}
    ~Shawty() {cout << _id << " dtor\n"; }
    int _id;
    static int id;
};
int Shawty::id = 0;

class B {
public:
    auto as_tuple() && {return std::make_tuple(std::move(_s1), std::move(_s2));}
    auto as_tuple2() && {return std::forward_as_tuple(std::move(_s1), std::move(_s2));}

private:
    Shawty _s1, _s2;
};

struct S {
    Shawty _s1, _s2;
};

int main() {
    std::cout << "----------\n";
    auto [s1, s2] = B().as_tuple2();
    std::cout << "---------\n";
    auto tpl1 = B().as_tuple2();
    std::cout << "----------\n";
    std::tuple<Shawty, Shawty> tpl2 = B().as_tuple2();
    std::cout << "----------\n";

    std::cout << std::get<0>(tpl1)._id << '\n';
    std::cout << std::get<1>(tpl1)._id << '\n';
    std::cout << std::get<0>(tpl2)._id << '\n';
    std::cout << std::get<1>(tpl2)._id << '\n';
    std::cout << s1._id << '\n';
    std::cout << s2._id << '\n';

    std::cout << "--struct--\n";
    auto [s3, s4] = S{};
    std::cout << s3._id << '\n';
    std::cout << s4._id << '\n';
    std::cout << "----------\n";

    return 0;
}

Ответы [ 2 ]

0 голосов
/ 13 сентября 2018

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

class B {
public:
    template<std::size_t I, class Self,
        std::enable_if_t< std::is_same_v<B, std::decay_t<Self>>, bool> = true
    >
    friend constexpr decltype(auto) get(Self&& self) {
        if constexpr(I==0)
        {
            using R = decltype(std::forward<Self>(self)._s1)&&;
            return (R)std::forward<Self>(self)._s1;
        }
        else if constexpr(I==1)
        {
            using R = decltype(std::forward<Self>(self)._s2)&&;
            return (R)std::forward<Self>(self)._s2;
        }
    }
private:
    Shawty _s1, _s2;
};

namespace std {
    template<>
    struct tuple_size<::B>:std::integral_constant<std::size_t, 2> {};
    template<std::size_t N>
    struct tuple_element<N, ::B>{using type=Shawty;};
}

Тестовый код:

int main() {
    std::cout << "----------\n";
    {
        auto&& [s1, s2] = B();
    }
}

вывод:

 ----------
 0 ctor
 1 ctor
 1 dtor
 0 dtor

Живой пример .

Это лучшее, что я могу сделать.Обратите внимание, что s1 и s2 являются ссылками на расширенную версию B.

.
0 голосов
/ 13 сентября 2018

Нет. Невозможно продлить срок жизни более чем одного члена за пределы срока службы суперобъекта.

Таким образом, единственный способ «получить» элементы без копирования - сохранить суперобъект в действии и обращаться к ним:

// member function
auto as_tuple3() & {
    return std::make_tuple(std::ref(_s1), std::ref(_s2));
}

// usage
B b;
auto [s1, s2] = b.as_tuple3();

Пример продления времени жизни объекта путем привязки ссылки к одному члену. Обратите внимание, что для этого необходимо, чтобы член был доступен с того места, где привязана ссылка (не в вашем примере, где член является приватным):

auto&& s1 = B{}._s1;
...