Подтвердить что ты не робот

Могу ли я назначить указатель данных элемента производному типу?

Это, вероятно, лучше всего показывает пример кода. Следующая команда не скомпилируется с g++:

struct Base {
};

struct Derived : public Base {
};

struct Container {
    Derived data_;
};

int main(void) {
    Base Container::*ptr = &Container::data_;
}

Я получаю следующую ошибку: invalid conversion from 'Derived Container::*' to Base Container::*'. Не разрешено ли этим языком? Это ошибка компилятора? Я использую неправильный синтаксис?

Пожалуйста, помогите!

Некоторые сведения о том, почему я пытаюсь это сделать: у меня есть несколько частей данных элемента, которые я хочу использовать в первую очередь как их производные типы, но я хочу, чтобы они могли заполнять их через какой-то общий код. Данные будут поступать в произвольном порядке и иметь строчную метку, которую я бы использовал для выбора соответствующих данных элемента для заполнения. Я планировал создать std::map<std::string, Base Container::*> для назначения данных каждому члену через общий интерфейс. Я бы хотел избежать создания гигантской конструкции if else для поиска правильных данных элемента.

4b9b3361

Ответ 1

Это не ошибка компилятора, вы не можете этого сделать. (Но вы можете назначить Base:: * на Derived:: *).

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

Ответ 2

Указатели на элементы в С++ не являются наглядными указателями, но скорее похожими на смещения заданного члена и специфичны для этого типа, поэтому то, что вы пытаетесь сделать, на самом деле не поддерживается.

Здесь достойная дискуссия на Stackoverflow С++: указатель на элемент данных класса.

Ответ 3

Есть много довольно сложных, некоторые не очень хорошо объяснены, и несколько плоских неправильных ответов в этой теме.

Но проблема, мне кажется, в том, что в Container нет члена Base - есть член Derived. Вы не можете этого сделать:

Base Container::*ptr = &Container::data_;

... по той же причине вы не можете этого сделать:

int a;
long* pl = &a;

Во втором примере объект не является long, это a int. Аналогично, в первом примере объект не является Base, это a Derived.

Как возможная тангенциальная точка, мне кажется, что вы действительно хотите сделать, чтобы Base был абстрактным классом и имел Container элемент Base*, а не член Derived.

Ответ 4

Вам просто нужно написать:

Base* ptr = &container.data_;

но container должен быть экземпляром container, поэтому вам нужно создать где-то одну переменную этого типа.

Ответ 5

Вы не можете преобразовать C:: * A в C:: * B, даже если между A и B. существует преобразование.

Однако вы можете сделать this:

struct Base
{
    virtual ~Base() {}
    virtual void foo() { std::cout << "Base::foo()\n"; }
};

struct Derived : Base
{
    void foo() { std::cout << "Derived::foo()\n"; }
};

struct Bar
{
    Base* x;

    Bar() : x(new Derived) {}
};

int main()
{
    Bar b;
    Base* Bar::*p = &Bar::x;
    (b.*p)->foo();
}

Ответ 6

Вам нужно static_cast выполнить это преобразование, как показано в 5.3.9/9. Эта причина заключается в том, что он действует как static_cast от указателя родительского объекта к указателю на дочерние объекты. Другими словами, включение указателя на производный член в элемент-указатель-родитель позволит вам получить доступ к несуществующему производному члену от родительского объекта или указателя. Если бы стандарт разрешил это автоматически, было бы легко испортиться и попытаться получить доступ к дочернему элементу класса, который не соответствует соответствующему типу дочернего элемента (который содержит указанный член).

Без дополнительной информации это звучит так, как будто вам нужен другой/лучший интерфейс конструктора/набора в вашем классе Base вместо того, чтобы пытаться использовать здесь указатели.

Ответ 7

Я думаю, что вам нужен "контейнер", т.е. структура, которая имеет только указатели:

struct Container{
    Base* derivedAdata_;
    Base* derivedBdata_;
    ...
};

Теперь каждый из членов, которых вы знаете, имеет определенный тип (т.е. DerivedA, DerivedB и т.д.), чтобы впоследствии их можно было отбросить позже.

Но сначала вы получаете данные (в произвольном порядке), но с именем строки, поэтому у вас должна быть карта:

std::map<std::string, Base* Container::*>

И вы, должно быть, уже заполнили карту:

myMap["DerivedA"] = &Container::derivedAdata;
...

Теперь данные поступают, и вы начинаете заполнять контейнер:

instance.*(myMap[key]) = factory(key, data);

myMap[key] выбирает правильный элемент контейнера, а factory(key,data) создает экземпляры.

Кстати, вы могли бы просто иметь карту как свой контейнер: std::map<std::string, Base*>

Ответ 8

Что касается исходной проблемы, вы можете сделать это, указав на функции, вместо того, чтобы вводить базовые классы.

class Container {
public:
  void set(std::string const& label, std::string const& value);

  void setName(std::string const& value) { _name = value; }
  void setAge(std::string const& age) {
    _age = boost::lexical_cast<size_t>(age);
  }

private:
  std::string _name;
  size_t _age;
};

Как реализовать set затем?

// container.cpp
typedef void (Container::*SetterType)(std::string const&);
typedef std::map<std::string, SetterType> SettersMapType;

SettersMapType SettersMap =
  boost::assign::map_list_of("name", &Container::setName)
                            ("age", &Container::setAge);

void Container::set(std::string const& label, std::string const& value) {
  SettersMapType::const_iterator it = SettersMap.find(label);
  if (it == SettersMap.end()) { throw UnknownLabel(label); }

  SetterType setter = it->second;
  (this->*setter)(value);
}

Ответ 9

struct Container {
   Derived data_; 
};  

int main(void) 
{
   Base Container::*ptr = &Container::data_;
} 

Первая проблема заключается в том, что Container не имеет члена, называемого ptr

Container container_object;
Base *ptr = container_object.data_;

Будет работать. Обратите внимание, что для создания элемента data_ должен быть контейнерный объект, и его необходимо будет опубликовать.

Альтернативой было бы, чтобы производный:: data_ был статическим членом.