我可以将成员数据指针分配给派生类型吗?

发布于 2024-11-07 08:31:58 字数 610 浏览 0 评论 0原文

这可能最好用示例代码来展示。以下内容无法使用 g++ 进行编译:

struct Base {
};

struct Derived : public Base {
};

struct Container {
    Derived data_;
};

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

我收到以下错误:从“Derived Container::*”到 Base Container::*' 的转换无效。 这是语言不允许的吗?这是编译器错误吗?我使用了错误的语法吗?

请帮忙!

关于我为什么要这样做的一些背景:我有几个成员数据块,我想主要将它们用作它们的派生类型,但我希望能够通过一些通用代码来填充它们。数据将以任意顺序出现,并具有一个字符串标签,我将使用它来选择要填充的适当成员数据。我计划创建一个 std::map 通过通用接口将数据分配给每个成员。我想避免使用巨大的 if else 构造来查找正确的成员数据。

This is probably best shown with example code. The following fails to compile with g++:

struct Base {
};

struct Derived : public Base {
};

struct Container {
    Derived data_;
};

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

I get the following error: invalid conversion from 'Derived Container::*' to Base Container::*'.
Is this not allowed by the language? Is this a compiler bug? Am I using the wrong syntax?

Please help!

Some background as to why I'm trying to do this: I have several member data pieces that I want to use primarily as their derived types, but I want to be able to populate them through some common code. Data will be coming in an arbitrary order and have a string label that I would use to select the appropriate member data to populate. I was planning on creating a std::map<std::string, Base Container::*> to assign data to each member through a common interface. I'd like to avoid have a giant if else construct to find the right member data.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

本宫微胖 2024-11-14 08:31:58

这不是编译器错误,你不能这样做。 (但是您可以将 Base::* 分配给 Derived::*)。

我没有看到任何限制的充分理由(除了处理多重继承的情况,这会使成员指针的表示更加复杂)。

This is not a compiler bug, you can't do that. (But you can assign a Base::* to a Derived::*).

I don't see any good reason for the limitation (excepted that to handle the case of multiple inheritance, that would complicate even more the representation of a member pointer).

没企图 2024-11-14 08:31:58

这篇文章中有很多相当复杂的、一些解释不清的答案,以及一些完全错误的答案。

但在我看来,问题在于 Container 中根本没有 Base 成员——只有一个 Derived 成员。你不能这样做:

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

...出于同样的原因,你不能这样做:

int a;
long* pl = &a;

在第二个示例中,对象不是 long,它是 int。同样,在第一个示例中,对象不是Base,而是Derived

作为一个可能的切线点,在我看来,您真正想做的是让 Base 成为一个抽象类,并让 Container 有一个 Base*< /code> 而不是 Derived 成员。

There are a lot of fairly complex, some not-well-explained, and a few flat wrong answers in this thread.

But the problem, it seems to me, is that there simply isn't a Base member within Container -- there is a Derived member. You can't do this:

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

...for the same reason you can't do this:

int a;
long* pl = &a;

In the second example, the object isn't a long, it's an int. Similarly, in the first example the object isn't a Base, it's a Derived.

As a possibly tangential point, it seems to me like what you really want to do is have Base be an abstract class, and have Container have a Base* rather than a Derived member.

粉红×色少女 2024-11-14 08:31:58

C++ 中指向成员的指针实际上并不是指针,而更像是给定成员的偏移量,并且特定于类型,因此您尝试执行的操作并未得到真正支持。

这里有一个关于 Stackoverflow C++:指向类数据成员的指针的不错的讨论。

Pointers to members in C++ are not really pointers but more like offsets to given member and are specific to the type, so what you are trying to do is not really supported.

Here's a decent discussion here on Stackoverflow C++: Pointer to class data member.

吲‖鸣 2024-11-14 08:31:58

您只需要编写:

Base* ptr = &container.data_;

container 必须是 Container 的实例,因此您必须在某处创建该类型的一个变量。

You just need to write:

Base* ptr = &container.data_;

but container has to be an instance of Container, so you have to create one variable of that type somewhere.

节枝 2024-11-14 08:31:58

即使 A 和 B 之间可以进行转换,您也无法将 C::*A 转换为 C::*B。

但是,您可以执行

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();
}

You cannot convert C::*A to C::*B even if there is a conversion possible between A and B.

However, you can do 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();
}
柠北森屋 2024-11-14 08:31:58

您必须使用 static_cast 来执行此转换,如 5.3.9/9 中所示。这样做的原因是它充当从父对象指针到子对象指针的static_cast。换句话说,将指向派生成员的指针放入指向父成员的指针中将允许您从父对象或指针访问不存在的派生成员。如果标准自动允许这样做,那么很容易搞砸并尝试访问不属于适当子类型(包含所述成员)的类上的子成员。

如果没有更多信息,听起来您需要在 Base 类中使用不同/更好的构造函数/设置接口,而不是尝试在此处使用指向成员的指针。

You would have to static_cast to do this conversion as seen in 5.3.9/9. This reason for this is that it acts as a static_cast from parent object pointer to child object pointer would. In other words, putting a pointer to a derived member into a pointer-to-parent-member would allow you to possibly access a non-existent derived member from a parent object or pointer. If the standard allowed this automatically it would be easy to mess up and try to access a child member on a class that isn't of the appropriate child type (that contains said member).

Without more information it sounds like you need a different/better constructor/set interface in your Base class rather than trying to use pointers-to-member here.

紫罗兰の梦幻 2024-11-14 08:31:58

我认为你想要的是一个“容器”,即一个只有指针的结构:

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

I think what you want is a 'container', ie a struct which just has pointers:

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

Now each of the members you know to be of a specific type (ie DerivedA, DerivedB etc) so you can down-cast them later.

But first you are receiving data (in arbitrary order), but with a string name, so you should have a map:

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

And you must have already populated the map:

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

Now data arrives and you start populating the container:

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

myMap[key] picks the right member of the container and factory(key,data) creates instances.

btw you could just have a map as your container anyway:std::map<std::string, Base*>

沒落の蓅哖 2024-11-14 08:31:58

关于最初的问题,您可以使用指向函数的指针来完成此操作,而不是引入基类。

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);
}

Regarding the original issue, you can do this using pointer to functions, instead of introducing base classes.

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;
};

How to implement set then ?

// 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);
}
死开点丶别碍眼 2024-11-14 08:31:58
struct Container {
   Derived data_; 
};  

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

第一个问题是 Container 没有一个名为 ptr 的成员

Container container_object;
Base *ptr = container_object.data_;

可以工作。请注意,需要有一个容器对象来创建 data_ 成员,并且需要将其公开。

另一种方法是让 returned::data_ 成为静态成员。

struct Container {
   Derived data_; 
};  

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

The first problem is that Container doesn't have a member called ptr

Container container_object;
Base *ptr = container_object.data_;

Would work. Note that there needs to be a container object to create the data_ member and it would need to be made public.

The alternative would be for derived::data_ to be a static member.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文