Исключение копирования / перемещения при выводе участников из временного

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

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

class TP {
    T _t1, _t2;
};

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

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

На следующей площадке с использованием B::as_tuple2 члены умирают слишком рано, если результат не привязан к типу без ссылки, тогда члены перемещаются. 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;
}

Предположим, class TP { T t1, t2, t3; };, где вам нужны только t1, t3, как бы вы справились с этим (макет структуры)?

Jarod42 13.09.2018 18:35

Пример со структурированной привязкой используется, чтобы показать, что нечто подобное возможно. Я представляю, как записываю это как кортеж ссылок, чтобы я мог вернуть return tuple<T&&, T&&>{std::move(t1), std::move(t3)}; и позволить t2 умереть. Я бы рассмотрел возможность разрешить такие вещи для всех членов и оставить объект в неопределенном состоянии. Но случай с временными кажется проще, потому что кажется, что исходные члены могут просто привязаться к новым переменным и продлить срок их службы, как в случае с копированием. Как вы думаете, это может быть технически сложно или снижать производительность из-за разметки памяти?

luk32 13.09.2018 21:38
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
2
256
2

Ответы 2

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

Итак, единственный способ "получить" члены без копирования - это сохранить суперобъект живым и ссылаться на него:

// 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;

Однако возможно продлить время жизни члена один.

Jarod42 13.09.2018 18:37

@ Jarod42 Я этого не знал. Не могли бы вы дать ссылку?

eerorika 13.09.2018 18:40

Я бы сказал open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf § 15.2 стр. 284

Jarod42 13.09.2018 19:36

@xhamr это конкретное правило, похоже, было добавлено в черновик C++ 20, но, похоже, в основном для разъяснения / лучшей формулировки. Правило the temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference написано на C++ 17.

eerorika 13.09.2018 19:37

Почему не std::tie( _s1, _s2 )? Кажется короче и менее подвержен ошибкам. Также обратите внимание, что вы можете расширить TP для поддержки структурированной привязки и использовать ее для создания псевдонимов.

Yakk - Adam Nevraumont 13.09.2018 20:44

@ Yakk-AdamNevraumont std::tie не позволил бы вам определить их тип, не так ли?

eerorika 13.09.2018 21:21

@ user2079303 Почему бы и нет? Буквально замените возвращаемое выражение этим. Вы получаете тот же тип, в 3 раза меньше символов и меньше повторений.

Yakk - Adam Nevraumont 13.09.2018 21:34

Добавьте поддержку структурированной привязки к вашему типу 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 с увеличенным сроком службы.

Для справки, это решение реализует интерфейс для привязки структуры. Случай 2, описанный на cppreference, я правильно понимаю?

luk32 13.09.2018 21:37

@ luk32 Да; Я добавил преамбулу, чтобы сказать это, вероятно, с тех пор, как вы обновили страницу.

Yakk - Adam Nevraumont 13.09.2018 21:42

Другие вопросы по теме