Структурированные привязки для вашего собственного типа, который не является структурой или кортежем (через общедоступную функцию-член)

Я прохожу через Herb Sutter's

Путешествие: к более мощному и простому программированию на C++

Связывание структуры

Чтобы понять концепцию. Лучше всего написать программу, которую я пробовал, но получаю какую-то ошибку.

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

#include<iostream>
#include<string>
using namespace std;

class foobar {
public:
    foobar() { cout << "foobar::foobar()\n"; }
    ~foobar() { cout << "foobar::~foobar()\n"; }

    foobar( const foobar &rhs )
    { cout << "foobar::foobar( const foobar & )\n"; }
    void ival( int nval, string new_string ) { _ival = nval;s=new_string; }

private:
    int _ival;
    string s;
};

foobar f( int val,string new_string ) {
    foobar local;
    local.ival( val,new_string );
    return local;
}

template<> struct tuple_element<0,foobar> { using type = int; };
template<> struct tuple_element<1,foobar> { using type = string; };



 // 2. Now add get<> support (using C++17, because why not; it’s better
 // than =delete’ing the primary get<> template and adding specializations)
 template<int I>
 auto get(const foobar&x) {
 if      constexpr(I == 0) return x._ival;//'_ival' is a private member of 'foobar'
 else if constexpr(I == 1) return x.s;//'s' is a private member of 'foobar'
 }


int main(){
    foobar ml = f( 1024,"hello" );
    auto [ n, s] = f( 1024,"hello" );//Cannot decompose non-public member '_ival' o
    return 0;
}

Ошибка

if constexpr(I == 0) return x._ival;//'_ival' является закрытым членом 'foobar'

иначе, если constexpr(I == 1) возвращает x.s;//'s' является закрытым членом 'foobar'

auto [ n, s] = f( 1024,"hello" );//Невозможно разложить непубличный

Требуется помощь

1. Если кто-нибудь может уточнить, что он на самом деле пытается сделать в этих строках (пожалуйста, обратитесь по предоставленной ссылке)

// 2. Now add get<> support (using C++17, because why not; it’s better
// than =delete’ing the primary get<> template and adding specializations)
template<int I>
auto get(const S&) {
   if      constexpr(I == 0) return x.i;
   else if constexpr(I == 1) return string_view{x.c}; }
   else if constexpr(I == 2) return x.d;
}

2. Любое предложение, как исправить ошибку для приведенного выше примера


person Hariom Singh    schedule 26.08.2017    source источник
comment
Просто хочу попробовать, как использовать привязку структуры к классу с закрытыми данными.   -  person Hariom Singh    schedule 26.08.2017
comment
Что ж, ошибка кажется довольно очевидной: функция, не являющаяся членом, не являющаяся другом, get пытается получить доступ к закрытым членам класса.   -  person Igor Tandetnik    schedule 26.08.2017
comment
Прежде всего, не редактируйте ответы в своих вопросах. Во-вторых, не Эйнпоклум предложил эти изменения, а я.   -  person Barry    schedule 26.08.2017
comment
@Barry извините за это, не могли бы вы добавить полный ответ?   -  person Hariom Singh    schedule 26.08.2017


Ответы (2)


Исправление ошибок в примере Саттера

Я думаю, что это опечатка/глюк в сообщении в блоге Херба Саттера: он должен был сделать этих членов общедоступными, или предоставить для них геттеры, или сделать функцию std::get() другом.

Кроме того, похоже, что Херб забыл поставить "x" в сигнатуре функции...

Объяснение функции get

Функция, которую вы цитируете, аналогична тому, как std::get() работает для кортежей. Если у меня есть

std::tuple<int, std::string> t;

тогда

auto x { std::get<0>(t) }; // x is an integer
auto y { std::get<1>(t) }; // y is an std::string

и в примере Херба ему нужно иметь ту же работу для класса S, т.е. чтобы std::get<0>(s) возвращал первый элемент класса s, std::get<1>(s) возвращал второй элемент и т. д. Это необходимо, потому что иначе вы не можете использовать S для инициализации класса. структурированное связывание.

«Магия» в реализации Hebr заключается в том, что он возвращает значения разных типов из разных точек своей функции. Эта «магия» — эффект if constexpr. По сути, это означает, что компилятор игнорирует все, кроме синтаксиса нерелевантных ветвей. Итак, для I = 0 функция такова:

auto get(const S&) {
  if (true) return x.i;
  /* else if constexpr(I == 1) return string_view{x.c}; 
     else if constexpr(I == 2) return x.d;
   */
}

для I = 1 это

template<int I>
auto get(const S&) {
   if      (false) {/* return x.i; */ ; }
   else if (true) return string_view{x.c};
   /* else if constexpr(I == 2) return x.d; */
   }
}

и т.д. И auto выбирает соответствующий тип.

person einpoklum    schedule 26.08.2017
comment
Спасибо за предложение сделать функцию get общедоступной. Я все еще вижу проблему с auto [ n, s] = f ( 1024, привет ); Что-нибудь, что мне нужно изменить здесь - person Hariom Singh; 26.08.2017

Здесь много проблем.

Во-первых, чтобы претендовать на структурированные привязки, вам необходимо специализироваться tuple_size:

namespace std {
    template <> struct tuple_size<foobar> : std::integral_constant<size_t, 2> { };
}

Далее, ваши специализации tuple_element также должны быть в namespace std:

namespace std {
    template <> struct tuple_size<foobar> : std::integral_constant<size_t, 2> { };

    template <> struct tuple_element<0,foobar> { using type = int; };
    template <> struct tuple_element<1,foobar> { using type = std::string; };
}

Затем ваша get должна быть объявлена ​​как функция friend, если вы собираетесь получить доступ к закрытым членам, как обычно:

class foobar {
    template <int I> friend auto get(foobar const& );
};

Наконец, get() действительно имеет лучшие возвращаемые ссылки, иначе ваши привязки в конечном итоге будут делать удивительные вещи:

template<int I>
auto const& get(const foobar&x) {
    if      constexpr(I == 0) return x._ival;
    else if constexpr(I == 1) return x.s;
}

Вместо того, чтобы иметь дело с friendship, проще просто сделать get() общедоступным членом, а затем написать три необходимых перегрузки:

class foobar {
public:
    template <size_t I>
    auto& get() & {
        if constexpr (I == 0) return _ival;
        else if constexpr (I == 1) return s;
    }

    template <size_t I>
    auto const& get() const& {
        if constexpr (I == 0) return _ival;
        else if constexpr (I == 1) return s;
    }

    template <size_t I>
    auto&& get() && {
        if constexpr (I == 0) return std::move(_ival);
        else if constexpr (I == 1) return std::move(s);
    }
};

Также ival() как функция не имеет смысла. Ваш конструктор должен просто принимать аргументы.

person Barry    schedule 26.08.2017
comment
Сделал все изменения, но я получаю сообщение об ошибке Вызов get неоднозначен в auto [n, s] = f( 1024,hello ); - person Hariom Singh; 26.08.2017
comment
@HariomSingh Вам нужна еще одна перегрузка get() для возврата ссылок rvalue. - person Barry; 26.08.2017
comment
@Barry Может/должен ли std::get быть частью этого? Разве пользователь не ожидает этого чаще? В конце концов, у std::tuple нет метода get. - person user2023370; 20.01.2021
comment
@user2023370 user2023370 Нет, не должно. std::tuple находится в std, поэтому у него может быть std::get, но ваш кортеж не находится в std, поэтому он не должен. - person Barry; 20.01.2021
comment
Я рад согласиться с этим, но могу я спросить о ваших рассуждениях? tuple_size и tuple_element также находятся в std. Да, std::get — это перегрузка, которая отличается, но есть ли у вас чутье на это различие? Я не считаю это очевидным. Возможно, это просто так указано. Во-вторых, с общей точки зрения программирования мне было бы удобно, если бы std::get (например, std::tuple_size и std::tuple_element) работало как для моих собственных кортежеподобных объектов, так и для std::tuple. - person user2023370; 20.01.2021
comment
@user2023370 user2023370 Да, но tuple_size и tuple_element являются шаблонами классов - они должны специализироваться на std. Но ничто в std::get не указывает на то, что он предназначен для настройки. Вы не можете полагаться на какие-либо get в универсальном коде, поскольку структурированные привязки в любом случае позволяют им быть членами. - person Barry; 20.01.2021
comment
Счастливые дни: я только что наткнулся на P0846, который разрешает ADL для шаблонов в C++20, и поэтому правильный get можно найти. - person user2023370; 20.01.2021