13

这可能最好用示例代码来展示。以下无法使用 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结构来找到正确的成员数据。

4

9 回答 9

3

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

我看不出有什么好的限制理由(除了处理多重继承的情况,这会使成员指针的表示更加复杂)。

于 2011-05-16T13:27:58.800 回答
1

指向 C++ 中成员的指针并不是真正的指针,而更像是给定成员的偏移量,并且特定于类型,因此您尝试做的事情并不真正受支持。

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

于 2011-05-16T13:26:02.950 回答
1

在这个线程中有很多相当复杂的,一些没有很好解释的,还有一些简单的错误答案。

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

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

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

int a;
long* pl = &a;

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

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

于 2011-05-16T13:55:01.410 回答
0

你只需要写:

Base* ptr = &container.data_;

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

于 2011-05-16T13:21:46.817 回答
0

即使 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();
}
于 2011-05-16T13:39:01.930 回答
0

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

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

于 2011-05-16T13:39:38.467 回答
0

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

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*>

于 2011-05-16T13:43:00.043 回答
0

关于原始问题,您可以使用指向函数的指针来执行此操作,而不是引入基类。

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);
}
于 2011-05-16T14:36:00.080 回答
-1
struct Container {
   Derived data_; 
};  

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

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

Container container_object;
Base *ptr = container_object.data_;

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

另一种方法是将derived::data_ 设为静态成员。

于 2011-05-16T14:22:59.317 回答