1

Тема: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

Цей фраґмент нормально компілюється :

union U {
    public:
        int *const v;
    protected:
        int *_v;
};

Коли я до попереднього додаю це :

struct S : public U {
    S() {
        // _v = nullptr;
    }
};

то отримую помилку

Visual C++ написав:

Поимлка C2569 - 'U': "enum/union не може бути використаний як базовий клас"
Error C2569 - 'U': "enum/union cannot be used as a base class"

Що я хочу зробити :
Структуру, яка буде містити 2 різних представленя одного поля з різними модифікаторами доступу (публучне для користувача, і приватне або захищене для внутрішніх операцій описаних в межах структури).

Чи можливо в C++ зробити щось подібне ?

2 Востаннє редагувалося wander (12.11.2022 20:56:56)

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

В С++ заборонено напряму наслідуватися від union'нів, з деяких, не дуже очевидних, причин.

leofun01 написав:

Що я хочу зробити :
Структуру, яка буде містити 2 різних представленя одного поля з різними модифікаторами доступу (публучне для користувача, і приватне або захищене для внутрішніх операцій описаних в межах структури).

Чи можливо в C++ зробити щось подібне ?

Давайте разом подумаємо, навіщо вам це потрібно? Можна якийсь юз-кейс?
Чи це чисто академічне питання?

Подякували: leofun011

3

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

А нащо вам наслідування? Чому агрегації не вистачає?

Подякували: leofun011

4

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

wander написав:

Давайте разом подумаємо, навіщо вам це потрібно? Можна якийсь юз-кейс?
Чи це чисто академічне питання?

Скоріше академічне, а не спроба порішати якусь конкретну задачу. Я просто досліджую свої можливості в C++.
В C# мені дуже подобається користувати властивості. І я сподівався що в C++ можна зробити щось подібне.
Виклики методів get_* мені просто не подобаються, бо в моєму випадку воно буде виглядати

v = a->get_v()->get_b()->get_v();

це просто не зручно, і як на мене тільки погіршує читабельність.

Думав що знайшов рішеня :

struct S {
    union {
        public:
            int *const v;
        protected:
            int *_v;
    };
    S(int *const ptr) {
        _v = ptr; // ok
        // v = ptr; // error is ok
    }
};

але ні, маю іншу помилку

Visual C++ написав:

Поимлка C2626 - 'S::_v': приватний/захищений член даних є не дозволений в анонімному union
Error C2626 - 'S::_v': a private/protected data member is not allowed in an anonymous union

koala написав:

А нащо вам наслідування? Чому агрегації не вистачає?

Можна і агрегацію, мені би хоть якось.

5

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

struct S {
    U u;
    S() {
        u.v = nullptr;
    }
};
Подякували: leofun011

6 Востаннє редагувалося leofun01 (12.11.2022 22:16:05)

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

koala написав:
struct S {
    U u;
    S() {
        u.v = nullptr;
    }
};

То є так, але доступ до полів тоді буде через u.

v = a->u.v->b->u.v;

Я ще шукаю спосіб як його (u.) позбутися.
Щоб в мене було красиве

v = a->v->b->v;

або щось ще коротше.

7

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

leofun01 написав:

Щоб в мене було красиве

v = a->v->b->v;

або щось ще коротше.

Якщо у вас з'являється такий ланцюжок викликів, то я б переглянув те, як ви працюєте з об'єктами в цілому.

leofun01 написав:

Я ще шукаю спосіб як його (u.) позбутися.

Ну, зробіть тоді так

struct A {
    int* x() { ... } // public getter

protected:
    int* x_;
};

struct B : A {
    B() {
        x_ = nullptr;
    }
};
Подякували: leofun011

8

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

Ну то адаптер зробіть.

struct S;
union U {
    S *ptr;
    int num; //хай будуть різних типів, інакше нащо union
};
struct S {
    public:
    int get_num() {return u.num;}
    protected:
    S *get_ptr() {return u.ptr;}
    U u;
}
S *s = ...;
s->get_ptr()->get_num();
Подякували: leofun011

9

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

Використав перевизначеня операторів.
Вийшло трохи не так як я хотів, але загалом прийнятно.

template<typename T>
struct S {
    private:
        T *ptr;
    public:
        S(T *const ptr) : ptr(ptr) { // ok
            this->ptr = ptr; // ok
            // (*this)() = ptr; // error is ok
        }
        T *operator()() { return ptr; }
        T &operator*() { return *ptr; }
};

    int a = 7;
    S<int> si(&a);
    int *       ptr_a1 = si();
//  int *      &ptr_a2 = si(); // error is ok
    int *const  ptr_a3 = si();
    int *const &ptr_a4 = si();
    *si = 3;
//  si() = nullptr; // error is ok
    int b = *si;

На цьому тему можна вважати закритою.

10 Востаннє редагувалося wander (14.11.2022 17:27:14)

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

Нарешті, з'явилось трохи часу, щоб дописати нормальну відповідь.
1. Щодо union'ів. Технічних причин, чому б не можна було union використовувати як base-class, я не бачу. Наразі, це просто заборонено і все. Тобто, можливо причина в тому, що всі класи, які можна успадкувати, в С++ підтримують віртуальність (тобто може мати прихований вказівник на vmt), а union через його природу такий вказівник не зможе містити безпечно. Це моя спроба копнути чуть глибше, посилаючись на деталі реалізації. Що там собі думає комітет - я хз. Думаю, все зводиться до того, що union'и просто дуже погано підтримуються в С++ і існують лише для збереження сумісності з С. А, нормально ввести їх у С++ дядькам з комітету просто влом, вже неодноразово union'и щось ламали, але маємо, що маємо.
2. Глянув я на ці ваші проперті в С# і ось що вийшло у мене

template <typename T> struct Getter {
    Getter(T& v) : v_{v} { }

    operator T() const { return v_; }

protected:
    T& v_;
};

template <typename T> class Property : public Getter<T> {
    using Getter<T>::v_;
    using Getter<T>::Getter;

public:
    Property<T>& operator=(T v) {
        v_ = v;
        return *this;
    }
};

class Foo {
    int* ptr_ = nullptr;

public:
    Foo() : ptr{ptr_} {}

    Property<int*> ptr; // set/get
};


int main() {
    int a = 7;

    std::cout << "a = " << a << std::endl;

    Foo foo;
    foo.ptr = &a;

    int*        ptr_a1 = foo.ptr;
//  int*&       ptr_a2 = foo.ptr; // error is ok
    int* const  ptr_a3 = foo.ptr;
    int* const& ptr_a4 = foo.ptr;

    std::cout << "a = " << a << std::endl;
    std::cout << "ptr_a1 = " << ptr_a1 << std::endl;
    std::cout << "ptr_a3 = " << ptr_a3 << std::endl;
    std::cout << "ptr_a4 = " << ptr_a4 << std::endl;

    *foo.ptr = 3;

    std::cout << "a = " << a << std::endl;

    int b = *foo.ptr;

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
}

https://rextester.com/PAUDI7530

P.S. - думаю, можна зробити ще краще, але потрібно більше деталей (а-ля ТЗ)

Подякували: leofun011

11

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

wander, це шикарно, дякую. Щось мені зовсім з голови вилетіло, що оператор присвоєня (=) теж можна перевизначити, я ще й const провтикав.
Тільки треба буде потім перевірити скільки памяті займатиме екземпляр Foo, мені здається *ptr_ і *&v_ будуть зберігатись як окремі поля. Але задумка в будь-якому випадку цікава.

12 Востаннє редагувалося wander (15.11.2022 14:16:42)

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

leofun01 написав:

wander, це шикарно, дякую. Щось мені зовсім з голови вилетіло, що оператор присвоєня (=) теж можна перевизначити, я ще й const провтикав.
Тільки треба буде потім перевірити скільки памяті займатиме екземпляр Foo, мені здається *ptr_ і *&v_ будуть зберігатись як окремі поля. Але задумка в будь-якому випадку цікава.

Так, вам не здається. В мому випадку Property займатиме додаткову пам'ять, як окрема змінна. І я не певен, що цього можна буде позбутись, зберігши наявний (CSharpish) функціонал роботи. Хоча, маю підозру, що і в C# properties можуть впливати на розмір структури, за певних умов? Насправді відповідником до C# properties у С++ буде, щось типу такого

// (1)
class Foo {
public:
    void ptr(int* other); // set
    int* ptr() const;     // get
private:
    int* ptr_;
};

Можна ще спробувати скористатись друзями, щоб зекономити пам'ять, але прийдеться писати більше коду. Ідея, приблизно така

// (2)
struct Foo {
    class {
        int*  ptr_;
        int*& operator=(int* other) { return ptr_ = other; }
        friend struct Foo;
    public:
        operator int*() const { return ptr_; }
    } ptr;
    
    Foo(int* p) {
        ptr = p;
    }
};

Але, якщо чесно, я б так не робив :)
Краще намагатись писати "чистий" код на С++, спираючись на його функціонал з коробки, повірте. Потім це вам може боляче вистрілити в ногу.

Подякували: leofun011

13

Re: [C++][C2569][C2626] Помилка C2569 - не вдається наслідувати union

Проперті - це збочення. Коли ви викликаєте функцію, то маєте бачити, що конкретно тут викликається функція. А гетери-сетери без додаткової функціональності потрібні лише там, де в нащадках ця функціональність потрібна.
leofun01, гляньте, як енуми в Rust реалізовані, вам захочеться взагалі цю мову вивчити лише заради них :)

Подякували: leofun011