Структурное связывание
От: B0FEE664  
Дата: 28.09.23 15:50
Оценка: 39 (2)
Придумал задачку.
Что выведет следующая программа:
#include <type_traits>
#include <iostream>
#include <functional>
#include <mutex>


class TS
{
  public:
    TS(int x) : m_x{x}{};
    std::pair<std::lock_guard<std::mutex>, int&> Ref()
    {
        return std::pair<std::lock_guard<std::mutex>, int&>{m_mtx, m_x};
    }
    int GetX() 
    {
        std::lock_guard lock(m_mtx);
        return m_x;
    };
  private:
    std::mutex m_mtx{};  
    int m_x{};
};

int main()
{
    TS ts{1};
    {
      auto [lock, x] = ts.Ref();
      x+=1;
    }
    ts.Ref().second++;
    std::cout << ts.GetX() << std::endl;
    return 0;
}
И каждый день — без права на ошибку...
Re: Структурное связывание
От: andrey.desman  
Дата: 28.09.23 17:59
Оценка: +2
Здравствуйте, B0FEE664, Вы писали:

BFE>Придумал задачку.

А в чем предполагается засада?
Re[2]: Структурное связывание
От: flаt  
Дата: 28.09.23 20:44
Оценка:
Здравствуйте, andrey.desman, Вы писали:

BFE>>Придумал задачку.

AD>А в чем предполагается засада?

Полагаю, в копировании x, несмотря на возврат по ссылке. С другой стороны, этой фиче сто лет в обед, даром, что в структурное связывание завёрнута в данном примере.

upd: наоборот, копирования нет.
Отредактировано 02.10.2023 18:30 flаt . Предыдущая версия .
Re[3]: Структурное связывание
От: B0FEE664  
Дата: 28.09.23 21:54
Оценка:
Здравствуйте, flаt, Вы писали:

BFE>>>Придумал задачку.

AD>>А в чем предполагается засада?
F>Полагаю, в копировании x, несмотря на возврат по ссылке. С другой стороны, этой фиче сто лет в обед, даром, что в структурное связывание завёрнута в данном примере.

В копировании x? Откуда куда?
И каждый день — без права на ошибку...
Re[3]: Структурное связывание
От: andrey.desman  
Дата: 28.09.23 22:00
Оценка:
Здравствуйте, flаt, Вы писали:

F>Полагаю, в копировании x, несмотря на возврат по ссылке. С другой стороны, этой фиче сто лет в обед, даром, что в структурное связывание завёрнута в данном примере.


Т.е. фишка в том, что кто-то или даже многие подумают, что будет создан lvalue копией значения? Потому что auto без &&.
Re[2]: Структурное связывание
От: B0FEE664  
Дата: 28.09.23 22:05
Оценка:
Здравствуйте, andrey.desman, Вы писали:

BFE>>Придумал задачку.

AD>А в чем предполагается засада?

auto x = ...; // => x не ссылка
[x](){ /* x — не ссылка */ };
auto [x] = ...; // x — может ссылка, может нет

Всё лоГично!
И каждый день — без права на ошибку...
Re[3]: Структурное связывание
От: andrey.desman  
Дата: 28.09.23 22:11
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>auto x = ...; // => x не ссылка

BFE>[x](){ /* x — не ссылка */ };
Да.

BFE>auto [x] = ...; // x — может ссылка, может нет

Никогда не ссылка. Синоним. Или что-то я упустил. (массивы).

BFE>Всё лоГично!

Почти)
Отредактировано 28.09.2023 22:21 andrey.desman . Предыдущая версия . Еще …
Отредактировано 28.09.2023 22:19 andrey.desman . Предыдущая версия .
Re[4]: Структурное связывание
От: B0FEE664  
Дата: 29.09.23 00:04
Оценка:
Здравствуйте, andrey.desman, Вы писали:

BFE>>auto [x] = ...; // x — может ссылка, может нет

AD>Никогда не ссылка. Синоним. Или что-то я упустил. (массивы).
Что за "Синоним"?
Выглядит как ссылка, ведёт себя как ссылка:
#include <type_traits>
#include <iostream>
#include <functional>
#include <mutex>


int main()
{
    int a = 1;
    int b = 2;
    
    std::pair<int&, int&> ab{a,b};

    auto [a_, b_] = ab;
    a_ += 8;

    std::cout << "a=" << a << std::endl;
    std::cout << std::boolalpha;
    std::cout << "std::is_same<int&, decltype(a_)>::value = " << std::is_same<int&, decltype(a_)>::value << std::endl;

    return 0;
}

https://godbolt.org/z/csW6Exnz3


BFE>>Всё лоГично!

AD>Почти)
Так что программа-то напечатает?
И каждый день — без права на ошибку...
Re[5]: Структурное связывание
От: andrey.desman  
Дата: 29.09.23 01:07
Оценка:
Здравствуйте, B0FEE664, Вы писали:

AD>>Никогда не ссылка. Синоним. Или что-то я упустил. (массивы).

BFE>Что за "Синоним"?
Примерно как #define

BFE>Выглядит как ссылка, ведёт себя как ссылка.

Но не ссылка

BFE>Так что программа-то напечатает?

3
Re[5]: Структурное связывание
От: σ  
Дата: 29.09.23 08:31
Оценка: 6 (1)
BFE>>>auto [x] = ...; // x — может ссылка, может нет
AD>>Никогда не ссылка. Синоним. Или что-то я упустил. (массивы).
BFE>Что за "Синоним"?
BFE>Выглядит как ссылка, ведёт себя как ссылка:
https://timsong-cpp.github.io/cppwp/n4868/dcl.struct.bind#1.sentence-1
https://timsong-cpp.github.io/cppwp/n4868/dcl.struct.bind#4.sentence-9
Re[6]: Структурное связывание
От: B0FEE664  
Дата: 29.09.23 09:39
Оценка:
Здравствуйте, σ, Вы писали:

BFE>>Что за "Синоним"?

BFE>>Выглядит как ссылка, ведёт себя как ссылка:
σ>https://timsong-cpp.github.io/cppwp/n4868/dcl.struct.bind#1.sentence-1
σ>https://timsong-cpp.github.io/cppwp/n4868/dcl.struct.bind#4.sentence-9

Given the type Ti designated by std​::​tuple_­element<i, E>​::​type

— так это и есть ссылка для std::pair<int&, int&>
И каждый день — без права на ошибку...
Re[7]: Структурное связывание
От: andrey.desman  
Дата: 29.09.23 18:23
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>

BFE>Given the type Ti designated by std​::​tuple_­element<i, E>​::​type

BFE>- так это и есть ссылка для std::pair<int&, int&>

Для самой пары значение. Тут можно поговорить на тему guaranteed copy elision.

Да,еще упустил tuple-like классы, но смысл не меняется. Для них ссылки, но это по сути это то же кроме поддержки битовых полей.
Re: Структурное связывание
От: Кодт Россия  
Дата: 01.10.23 16:26
Оценка:
Здравствуйте, B0FEE664, Вы писали:

BFE>Придумал задачку.

BFE>Что выведет следующая программа:

Интересный приёмчик, может быть, даже потянет на идиому.
Но — в чём подвох?

Функция возвращает структуру со ссылочным типом внутри.
Прямой доступ — Ref().second — оперирует со ссылкой, тут ничего непредсказуемого нет.
Структурное связывание — делает теневую копию этой структуры и ссылки на её поля, очень грубо говоря,
auto [lock, x] = ts.Ref();

auto copy = ts.Ref();
auto& lock = copy.first;
auto& x = copy.second;

ссылка на ссылку равна ссылке.
И тоже никаких сюрпризов.
Перекуём баги на фичи!
Re[2]: Структурное связывание
От: Кодт Россия  
Дата: 01.10.23 17:04
Оценка: 34 (3)
К>Интересный приёмчик, может быть, даже потянет на идиому.

https://gcc.godbolt.org/z/5cqYMeWGz

Пусть структура из двух элементов отдаёт только данные, а сопровождающий lock — не отдаёт. Ибо нефиг.
template<class Guard, class Data>
struct guarded {
    Guard g;
    Data d;

    template<size_t I> auto& get() requires (I==0) {
        return d;
    }
    template<size_t I> auto& get() const requires (I==0) {
        return d;
    }
};

namespace std {
template<class G, class D> struct tuple_size<guarded<G,D>> {
    static constexpr size_t value = 1;
};
template<class G, class D> struct tuple_element<0, guarded<G,D>> {
    using type = D&;
};
}  // namespace std


тогда
struct lock {
    lock() { std::cout << "lock!" << std::endl; }
    lock(const lock&) = delete;
    ~lock() { std::cout << "unlock!" << std::endl; }
};

struct value {
    void touch() { std::cout << "touch " << this << std::endl; }
};

int main() {
    value x;
    std::cout << "value " << &x << std::endl;

    using G = guarded<lock,value&>;

    std::cout << "===" << std::endl;
    {
        G{{}, x}.d.touch();
    }
    std::cout << "===" << std::endl;
    {
        auto [y] = G{{}, x};
        y.touch();
    }
    std::cout << "===" << std::endl;

    auto g = [&x]() { return G{{}, x}; };
    {
        auto [y] = g();
        y.touch();
    }
    std::cout << "===" << std::endl;
}


Осталось придумать, как это всё покрасивее обмазать синтаксическим сахаром, — помимо собственно структурного связывания.

Может быть, CTAD прикрутить, поддержать отдельно rvalue и lvalue Data (или обойтись std::ref, например)...
Перекуём баги на фичи!
Re[8]: Структурное связывание
От: B0FEE664  
Дата: 02.10.23 10:15
Оценка:
Здравствуйте, andrey.desman, Вы писали:

BFE>>

BFE>>Given the type Ti designated by std​::​tuple_­element<i, E>​::​type

BFE>>- так это и есть ссылка для std::pair<int&, int&>
AD>Для самой пары значение.
В каком смысле?

AD> Тут можно поговорить на тему guaranteed copy elision.

AD>Да,еще упустил tuple-like классы, но смысл не меняется. Для них ссылки, но это по сути это то же кроме поддержки битовых полей.

Ну как же не меняется?
Вот тут:
    int a = 1;
    int b = 2;
    
    std::pair<int&, int&> ab{a,b};

    auto [a_, b_] = ab;
    a_ += 8;

a_ — это ссылка int&
b_ — это ссылка int&

Согласно приведённой σ ссылке на стандарт,
a_ привязывается к скрытой переменной r1 тип которой int&& или int&&& (-> int&) и которая ссылается на поле скрытой переменной e, а вот e — это копия структуры ab типа std::pair<int&, int&>.
И каждый день — без права на ошибку...
Re: Структурное связывание
От: Pzz Россия https://github.com/alexpevzner
Дата: 02.10.23 10:35
Оценка: -1
Здравствуйте, B0FEE664, Вы писали:

BFE>Придумал задачку.

BFE>Что выведет следующая программа:

Фигню какую-нибудь.

А в чем вообще ценность таких задачек? Когда тебе 18, то кажется, что код надо писать, проходя по грани человеческих познаний в области языка программирования. С опытом приходит понимание, что код надо писать так, чтобы любой юниор его прочел и правильно понял. И это гораздо сложнее, на самом деле.

А что выведет именно эта програмка — ну, можно запустить и посмотреть...
Re[9]: Структурное связывание
От: andrey.desman  
Дата: 02.10.23 10:55
Оценка:
Здравствуйте, B0FEE664, Вы писали:

AD>>Для самой пары значение.

BFE>В каком смысле?

В смысле копии пары.

AD>>Да,еще упустил tuple-like классы, но смысл не меняется. Для них ссылки, но это по сути это то же кроме поддержки битовых полей.

BFE>Ну как же не меняется?

Так и не меняется.

BFE>Согласно приведённой σ ссылке на стандарт,

BFE>a_ привязывается к скрытой переменной r1 тип которой int&& или int&&& (-> int&) и которая ссылается на поле скрытой переменной e, а вот e — это копия структуры ab типа std::pair<int&, int&>.

Я так и написал. О чем спор?
Re[2]: Структурное связывание
От: B0FEE664  
Дата: 02.10.23 12:15
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Интересный приёмчик, может быть, даже потянет на идиому.

Это развитие вот такого подхода.

template<typename T>
class SafeData
{
   public:
      ...
      T Get()
      {
         std::lock_guard<std::mutex> lock(mtx);
         return data;
      }
      void Set(const T& value)
      {
         std::lock_guard<std::mutex> lock(mtx);
         data = value;
      }

      template<class Fn>
      auto Access(Fn&& fnAccess)
      {
         std::lock_guard<std::mutex> lock( mtx );
         return const_access(std::forward<Fn>(fnAccess));
      }

      template<class Fn>
      auto Transform(Fn&& op)
      {
         std::lock_guard<std::mutex> lock( mtx );
         return op(data);
      }
   private:
      std::mutex  mtx;
      T           data;
};


В редких случаях есть желание получить опасный прямой доступ к data не через Access/Transform, а напрямую. Сначала я написал LockGuard друзей SafeData, а потом подумал, что можно обойтись парой функций:
    std::pair<std::lock_guard<std::mutex>, T&> LockWrite()
    {
      return std::pair<std::lock_guard<std::mutex>, int&>{mtx, data};
    }

    std::pair<std::lock_guard<std::mutex>, const T&> LockRead()
    {
      return std::pair<std::lock_guard<std::mutex>, const int&>{mtx, data};
    }


К>Но — в чём подвох?

А нет подвоха.

  Скрытый текст
К>Функция возвращает структуру со ссылочным типом внутри.
К>Прямой доступ — Ref().second — оперирует со ссылкой, тут ничего непредсказуемого нет.
К>Структурное связывание — делает теневую копию этой структуры и ссылки на её поля, очень грубо говоря,
К>
К>auto [lock, x] = ts.Ref();

К>auto copy = ts.Ref();
К>auto& lock = copy.first;
К>auto& x = copy.second;
К>

К>ссылка на ссылку равна ссылке.
К>И тоже никаких сюрпризов.

Для меня просто было неожиданностью, что
auto a = ...
и
auto [b] = ...
так различаются.

Я бы ожидал такое:
auto [b] = ... // копия поля
auto [&b] = ... // ссылка на поле
И каждый день — без права на ошибку...
Re[2]: Структурное связывание
От: B0FEE664  
Дата: 02.10.23 12:31
Оценка:
Здравствуйте, Pzz, Вы писали:

BFE>>Придумал задачку.

BFE>>Что выведет следующая программа:
Pzz>Фигню какую-нибудь.
Да — не, ровно то, что должно, без подвохов.

Pzz>А в чем вообще ценность таких задачек?

Вот, думаю в продакшен добавить...

Pzz>Когда тебе 18, то кажется, что код надо писать, проходя по грани человеческих познаний в области языка программирования.

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

Pzz>А что выведет именно эта програмка — ну, можно запустить и посмотреть...

Это вам не Паскаль.
И каждый день — без права на ошибку...
Re[3]: Структурное связывание
От: Pzz Россия https://github.com/alexpevzner
Дата: 02.10.23 13:42
Оценка:
Здравствуйте, B0FEE664, Вы писали:

Pzz>> С опытом приходит понимание, что код надо писать так, чтобы любой юниор его прочел и правильно понял. И это гораздо сложнее, на самом деле.

BFE>Некое противоречие есть в ваших словах.
BFE>Или юниор младше 18, либо ему будет интересно читать такой код.

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

Pzz>>А что выведет именно эта програмка — ну, можно запустить и посмотреть...

BFE>Это вам не Паскаль.

А что, C++ дошел уже до того, что программы на нем и запустить стало невозможно?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.