сопзЬ * сопзЬ * член зменнай ініцыялізацыі C ++

У мяне паўсталі праблемы з ініцыялізацыяй гэтай структуры (спрошчана, напрыклад)

struct S{ const float * const * const data;};

У прынцыпе ў мяне ёсць буфер буфераў паплаўкоў, і я выкарыстоўваю сопзЬ, каб забяспечыць хтосьці з дапамогай S нічога не можа змяніць гэты карыстальнік (толькі для чытання).

My problem is that this is complicated and hard to read to initialize, I would like to use a lambda that return an const S, and so I could initialize members in my lambda by writing the member name : s.data = ptr;

Цяпер гэты код з'яўляецца складаным, і мне цікава, што можа быць лепшым рашэннем.

AFAIK, які мае структура S {паплавок ** дадзеныя;} а Const S не будзе абараняць, як эфектыўна ўтрыманне элемента, я не мог змяніць S :: дадзеныя , але я мог бы змяніць * S :: дадзеныя .

<�Моцны> Як я павінен рабіць?

Дзякуй

1
Чаму б не выкарыстоўваць структура S {сопзЬ паплаўка * сопзЬ * дадзеныя; } ? Гэта дазволіць вам ініцыялізаваць S досыць лёгка, але вы можаце перадаць Const S * вакол ...
дададзена аўтар Dietrich Epp, крыніца
Чаму б не выкарыстоўваць структура S {сопзЬ паплаўка * сопзЬ * дадзеныя; } ? Гэта дазволіць вам ініцыялізаваць S досыць лёгка, але вы можаце перадаць Const S * вакол ...
дададзена аўтар Dietrich Epp, крыніца
Ды менавіта ў цяперашні час у мяне ёсць пэўны CTOR, але я хацеў бы, каб не сябры константного я раблю так: herbsutter.com/2013/04/05/…
дададзена аўтар dzada, крыніца
Дзякуй, але я б аддаў перавагу, каб ведаць, што я м дурныя і ёсць простае рашэнне, але я не знайшоў яго праз 3 дні
дададзена аўтар dzada, крыніца
Дзякуй, але я б аддаў перавагу, каб ведаць, што я м дурныя і ёсць простае рашэнне, але я не знайшоў яго праз 3 дні
дададзена аўтар dzada, крыніца
Радок s.data = PTR; ніколі не будзе кампіляваць, незалежна ад таго, што вы робіце, таму што дадзеныя гэта Const і не могуць быць прызначаныя. Вы павінны змяніць свой дызайн, калі вы хочаце, каб зрабіць гэта.
дададзена аўтар Seth Carnegie, крыніца
гэтае пытанне з'яўляецца фантастычным :-) ... +1
дададзена аўтар 6502, крыніца

7 адказы

Чаму б проста не выдаліць апошні сопзЬ ?

struct S{ const float * const * data;};

Такім чынам, вы можаце ініцыялізаваць дадзеныя , як вам падабаецца, і яна да гэтага часу не можа быць выкарыстана для змены ўсё, што ён паказвае на .

data itself can be modified, but should that be prevented, it should simply be private.

3
дададзена
Няма, таму што ў гэтым Cate карыстальнік гэтай структуры (што я хачу абараніць) можа змяніць самі дадзеныя, а затым усё буферы ўнутры дадзеных і ўсе тыя, у дадзеных [х] будзе цячы. Я згодны з вамі рашэннем было б зрабіць дадзеныя прыватнага і дадаць кучу функцый геттерных, але калі я павінен дасягнуць гэтага ускрайку я аддаю перавагу проста ініцыялізаваць яго ў канструктару. Мой рэальны пытанне заключаецца ў тым, каб зрабіць інтэрфейс больш ясным, і, магчыма, выкарыстоўваць лямбда для ініцыялізацыі майго константного чальца.
дададзена аўтар dzada, крыніца
Так, я хачу, каб зрабіць яго лёгка і ясна, каб стварыць структуры. (Я магу з відавочным CTOR, спіс инициализаторов, але гэта не так ясна, як struct.member = ххх ў лямбда) АЛЕ Пасля таго, як мая структура пабудавана і перадаецца ў функцыю, напрыклад, я хачу зрабіць гэта немагчыма змяніць небудзь дадзеныя альбо якія-небудзь дадзеныя [х] або дадзеныя [х] [у]. Я змяніў тут мой адказ, таму што перачытваючы яго, я думаю, што гэта можа быць лепшым падыходам. Што вы ўсё людзі думаюць?
дададзена аўтар dzada, крыніца
@dzada: Такім чынам, вы што змяніць дадзеныя лёгка, але вы не хочаце, каб іншыя былі ў стане зрабіць тое ж самае ээ? У вас ёсць два варыянты: 1) зрабіць яго закрытым, можа быць, у падклас; 2) зрабіць яго Const і ініцыялізаваць яго нейкім чынам (але вы ўжо ведаеце, што).
дададзена аўтар rodrigo, крыніца

Чаму б проста не выдаліць апошні сопзЬ ?

struct S{ const float * const * data;};

Такім чынам, вы можаце ініцыялізаваць дадзеныя , як вам падабаецца, і яна да гэтага часу не можа быць выкарыстана для змены ўсё, што ён паказвае на .

data itself can be modified, but should that be prevented, it should simply be private.

3
дададзена
Няма, таму што ў гэтым Cate карыстальнік гэтай структуры (што я хачу абараніць) можа змяніць самі дадзеныя, а затым усё буферы ўнутры дадзеных і ўсе тыя, у дадзеных [х] будзе цячы. Я згодны з вамі рашэннем было б зрабіць дадзеныя прыватнага і дадаць кучу функцый геттерных, але калі я павінен дасягнуць гэтага ускрайку я аддаю перавагу проста ініцыялізаваць яго ў канструктару. Мой рэальны пытанне заключаецца ў тым, каб зрабіць інтэрфейс больш ясным, і, магчыма, выкарыстоўваць лямбда для ініцыялізацыі майго константного чальца.
дададзена аўтар dzada, крыніца
Так, я хачу, каб зрабіць яго лёгка і ясна, каб стварыць структуры. (Я магу з відавочным CTOR, спіс инициализаторов, але гэта не так ясна, як struct.member = ххх ў лямбда) АЛЕ Пасля таго, як мая структура пабудавана і перадаецца ў функцыю, напрыклад, я хачу зрабіць гэта немагчыма змяніць небудзь дадзеныя альбо якія-небудзь дадзеныя [х] або дадзеныя [х] [у]. Я змяніў тут мой адказ, таму што перачытваючы яго, я думаю, што гэта можа быць лепшым падыходам. Што вы ўсё людзі думаюць?
дададзена аўтар dzada, крыніца
@dzada: Такім чынам, вы што змяніць дадзеныя лёгка, але вы не хочаце, каб іншыя былі ў стане зрабіць тое ж самае ээ? У вас ёсць два варыянты: 1) зрабіць яго закрытым, можа быць, у падклас; 2) зрабіць яго Const і ініцыялізаваць яго нейкім чынам (але вы ўжо ведаеце, што).
дададзена аўтар rodrigo, крыніца

Рэкамендуемы спосаб дадаць канструктар S . Гэта дазваляе ўсталяваць значэнне дадзеных у спісе инициализатора т е р.

struct S
{
    explicit S(const float *const *const d) : data(d) {}
    const float * const * const data;
};

S GetS()
{
    float **data = GetData();

    return S(data);
}

Калі вы хочаце абмежаваць, хто можа змяніць S :: дадзеныя пасля яго ініцыялізацыі вы можаце акно пераменная член і выкарыстоўваць дружбу, каб дазволіць доступ. Для гэтага патрабуецца инкапсулирования дадзеныя элемента ў дадатковай структуры, якая забяспечвае аператары пераўтварэння і прызначэння.

struct Outer
{
    struct S
    {
    private:

        struct ConstBox
        {
            friend Outer;

            ConstBox(const ConstBox& other) : data_(other.data_) {}
            explicit ConstBox(const float *const *const data) : data_(data) {}
            operator const float* const*() const { return data_; }

        private:

            ConstBox& operator=(const float * const * data)
            {
                data_ = data;
                return *this;
            }

            const float * const * data_;
        };


    public:

        S() : data(nullptr) {}
        explicit S(const float *const *const d) : data(d) {}

        ConstBox data;
    };

    S DoSomething() const
    {
        S   s(nullptr);

        auto f = []() -> S
        {
            S s;

            s.data = new float*[10];

            return s;
        };

        return f();
    }
};


typedef Outer::S S;

void FailTest()
{
    S   s;

    s.data = nullptr;//<-- fails
    float** v1 = s.data;//<-- fails
    const float** v1 = s.data;//<-- fails
   //These are ok
    const float* const* v2 = s.data;
}
1
дададзена
Відавочны CTOR: так, я згодны. На самай справе мне не трэба, каб зрабіць гэта, як згенераваны спіс инициализатора робіць гэта ўжо. Але мой пункт гледжання, з'яўляецца тое, што яна менш ясная для чытання, чым s.data = ххх асабліва калі ў вас ёсць шмат членаў у структуры Ды @rodrigo, то GetS() ня кампіляваць. Я каментаваў ідэю яўнага CTOR
дададзена аўтар dzada, крыніца
@ Bames53 я прапусціў ўзровень паказальніка ў больш раннім рэдагаванні;)
дададзена аўтар Captain Obvlious, крыніца
@dzada Зыходзячы з вашых дадатковых каментароў, якія я дадаў дадатковае рашэнне, якое больш дакладна адпавядае таму, што вы шукаеце.
дададзена аўтар Captain Obvlious, крыніца
@dzada Ёсць шмат розных спосабаў падысці да гэтай праблемы, выкарыстоўваючы канструктар толькі адзін. Я думаю, калі вы ўключылі прыклад таго, як вы хочаце стварыць і мутаваць S ўнутры вашага лямбда будзе лягчэй забяспечыць рашэнне бліжэй да таго, што вы шукаеце
дададзена аўтар Captain Obvlious, крыніца
Але S (дадзеныя) не будзе кампілявацца, так як дадзеныя не Вуснаў паплавок * сопзЬ * .
дададзена аўтар rodrigo, крыніца
@rodrigo паплавок ** можа быць ператвораны ў флоат сопзЬ * сопзЬ * . Гэта кампілюецца нармальна для мяне.
дададзена аўтар bames53, крыніца

Рэкамендуемы спосаб дадаць канструктар S . Гэта дазваляе ўсталяваць значэнне дадзеных у спісе инициализатора т е р.

struct S
{
    explicit S(const float *const *const d) : data(d) {}
    const float * const * const data;
};

S GetS()
{
    float **data = GetData();

    return S(data);
}

Калі вы хочаце абмежаваць, хто можа змяніць S :: дадзеныя пасля яго ініцыялізацыі вы можаце акно пераменная член і выкарыстоўваць дружбу, каб дазволіць доступ. Для гэтага патрабуецца инкапсулирования дадзеныя элемента ў дадатковай структуры, якая забяспечвае аператары пераўтварэння і прызначэння.

struct Outer
{
    struct S
    {
    private:

        struct ConstBox
        {
            friend Outer;

            ConstBox(const ConstBox& other) : data_(other.data_) {}
            explicit ConstBox(const float *const *const data) : data_(data) {}
            operator const float* const*() const { return data_; }

        private:

            ConstBox& operator=(const float * const * data)
            {
                data_ = data;
                return *this;
            }

            const float * const * data_;
        };


    public:

        S() : data(nullptr) {}
        explicit S(const float *const *const d) : data(d) {}

        ConstBox data;
    };

    S DoSomething() const
    {
        S   s(nullptr);

        auto f = []() -> S
        {
            S s;

            s.data = new float*[10];

            return s;
        };

        return f();
    }
};


typedef Outer::S S;

void FailTest()
{
    S   s;

    s.data = nullptr;//<-- fails
    float** v1 = s.data;//<-- fails
    const float** v1 = s.data;//<-- fails
   //These are ok
    const float* const* v2 = s.data;
}
1
дададзена
Відавочны CTOR: так, я згодны. На самай справе мне не трэба, каб зрабіць гэта, як згенераваны спіс инициализатора робіць гэта ўжо. Але мой пункт гледжання, з'яўляецца тое, што яна менш ясная для чытання, чым s.data = ххх асабліва калі ў вас ёсць шмат членаў у структуры Ды @rodrigo, то GetS() ня кампіляваць. Я каментаваў ідэю яўнага CTOR
дададзена аўтар dzada, крыніца
@dzada Зыходзячы з вашых дадатковых каментароў, якія я дадаў дадатковае рашэнне, якое больш дакладна адпавядае таму, што вы шукаеце.
дададзена аўтар Captain Obvlious, крыніца
@ Bames53 я прапусціў ўзровень паказальніка ў больш раннім рэдагаванні;)
дададзена аўтар Captain Obvlious, крыніца
@dzada Ёсць шмат розных спосабаў падысці да гэтай праблемы, выкарыстоўваючы канструктар толькі адзін. Я думаю, калі вы ўключылі прыклад таго, як вы хочаце стварыць і мутаваць S ўнутры вашага лямбда будзе лягчэй забяспечыць рашэнне бліжэй да таго, што вы шукаеце
дададзена аўтар Captain Obvlious, крыніца
Але S (дадзеныя) не будзе кампілявацца, так як дадзеныя не Вуснаў паплавок * сопзЬ * .
дададзена аўтар rodrigo, крыніца
@rodrigo паплавок ** можа быць ператвораны ў флоат сопзЬ * сопзЬ * . Гэта кампілюецца нармальна для мяне.
дададзена аўтар bames53, крыніца

@ Адказ CaptainObvious з'яўляецца правільным. Напішыце канструктар для S, прымаючы любыя аргументы, што трэба, і выкарыстоўваць Инициализатор члена, а не аператар прысвойвання, каб усталяваць «даныя».

0
дададзена
так, я ведаю, што я магу зрабіць гэта, мая праблема заключаецца ў тым, што не ясна, калі ў вас ёсць 20 членаў у структурах, каб напісаць іх у парадку, то больш зразумела пісаць strust.member =. Але калі я, што я паслабіць канстантнасцю абароны. Гэты інтэрфейс для кліента, я хачу гэта просты і абаронены
дададзена аўтар dzada, крыніца
Быў як раз збіраўся аднавіць яго таксама. Павінен зрабіць некалькі абнаўленняў, але, як вы сказалі, гэта пра тое, як правільна, як вы можаце атрымаць, не здымаючы канчатковага канстантнасцю CV спецификатор з дадзеныя
дададзена аўтар Captain Obvlious, крыніца
Я не ўпэўнены, што нам патрэбен адказ, які проста кажа, што нехта іншы адказ з'яўляецца правільным з най дадаў.
дададзена аўтар bames53, крыніца

@ Адказ CaptainObvious з'яўляецца правільным. Напішыце канструктар для S, прымаючы любыя аргументы, што трэба, і выкарыстоўваць Инициализатор члена, а не аператар прысвойвання, каб усталяваць «даныя».

0
дададзена
так, я ведаю, што я магу зрабіць гэта, мая праблема заключаецца ў тым, што не ясна, калі ў вас ёсць 20 членаў у структурах, каб напісаць іх у парадку, то больш зразумела пісаць strust.member =. Але калі я, што я паслабіць канстантнасцю абароны. Гэты інтэрфейс для кліента, я хачу гэта просты і абаронены
дададзена аўтар dzada, крыніца
Быў як раз збіраўся аднавіць яго таксама. Павінен зрабіць некалькі абнаўленняў, але, як вы сказалі, гэта пра тое, як правільна, як вы можаце атрымаць, не здымаючы канчатковага канстантнасцю CV спецификатор з дадзеныя
дададзена аўтар Captain Obvlious, крыніца
Я не ўпэўнены, што нам патрэбен адказ, які проста кажа, што нехта іншы адказ з'яўляецца правільным з най дадаў.
дададзена аўтар bames53, крыніца

З вашым спрошчаным прыкладам я б проста зрабіць:

struct S{ float const * const * const data;};

auto create_buffer() -> float const * const * {
    float **buf;
    /* ... compute buffer contents */
    return buf;
}

S s {create_buffer()};

Тым не менш, у каментары вы кажаце, што ў вас ёсць шмат членаў і ініцыялізацыі членаў на аснове таго, недастаткова ясна.

struct S { const A a; const B b; const C c; };

S s {x,y,z};//order based, not readable enough.

const members must be initialized as part of the object's construction. You must either specify them somehow in the initializer, or you must set their value in the class, so that they are set at construction time.


рашэнне 1

Адзін са спосабаў перадаць іх у час будаўніцтва, але і ў даступнай форме, каб выкарыстоўваць другі аб'ект, каб дапамагчы ініцыялізацыі:

struct S_initializer { A a; B b; C c; }

struct S {
  const A a; const B b; const C c;
  S(S_initializer &s) : a(s.a), b(s.b), c(s.c) {}
};

S make_S() {
  S_initializer s;
  s.a = x;
  s.b = y;
  s.c = z;
  return S{s};
}

Вышэй ўключае ў сябе некаторыя выводзяцца паўтараюцца, што вы можаце пазбегнуць, проста робячы памочнік ініцыялізацыі аб'екта канстантнасцю член S:

struct S {
  const S_initializer m;
  S(S_initializer &s) : m{s} {}
};

S make_S() {
  S_initializer s;
  s.a = x;
  s.b = y;
  s.c = z;
  return S{s};
}

Кампраміс у тым, што цяпер доступ да членаў S вы павінны мець дадатковы там:

A a = s.m.a;//versus just s.a;

рашэнне 2

Другі метад заснаваны на пашырэнні кампілятара; Хоць гэта і не стандартны C ++, GCC і ляск рэалізаваць C99 прызначаныя инициализаторами ў C ++. VC ++ не рэалізуе гэта.

S s { .a = x, .b = y, .c = z };
0
дададзена