【问题标题】:Can I assign a member data pointer to a derived type?我可以将成员数据指针分配给派生类型吗?
【发布时间】:2011-08-26 10:57:49
【问题描述】:

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

【问题讨论】:

  • 也许这只是你的例子,但在一般情况下,你不应该实现自己的容器。
  • 这不太可能是编译器错误。您是否尝试编译您发布的示例?在 main() 中,“&Container::data_”实际上不是一个实例,您没有“Container”的对象。
  • 对回答者的一般评论:回答前请了解会员指针。

标签: c++ member-pointers


【解决方案1】:

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

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

【讨论】:

  • 我看到了限制的充分理由——它不是Base
  • @John,您可以将 Derived* 分配给 Base*,但不能将 Derived Foo::* 分配给 Base Foo::*。除了实现复杂性之外,我没有看到任何原因。 (我认为不允许将 Foo Derived::* 分配给 Foo Base::* 的充分理由。您可以将 Foo Base::* 分配给 Foo Derived::*)。
【解决方案2】:

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

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

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

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

int a;
long* pl = &a;

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

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

【讨论】:

  • 我不明白你的 intlong 的类比如何成立;按照这个推理,那不是也不允许Base* b = &derived 吗?
【解决方案3】:

指向 C++ 中成员的指针不是 真正 指针,而更像是给定成员的 offsets 并且特定于类型,所以你想要做的并不是真的支持。

这是 Stackoverflow C++: Pointer to class data member 上的一个不错的讨论。

【讨论】:

    【解决方案4】:

    你只需要写:

    Base* ptr = &container.data_;
    

    但是container 必须是Container 的一个实例,所以你必须在某处创建一个该类型的变量。

    【讨论】:

    • 据我了解的问题,OP 的设计需要指向成员的指针。
    【解决方案5】:

    即使 A 和 B 之间可以转换,您也无法将 C::*A 转换为 C::*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 类中使用不同/更好的构造函数/设置接口,而不是尝试在此处使用指向成员的指针。

      【讨论】:

      • 您能否详细说明(可能有一个示例)如何将Derived Container::* 成员自动转换为Base Container::* 成员可能是不安全的?
      【解决方案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&lt;std::string, Base*&gt;

      【讨论】:

        【解决方案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_ 成员,并且需要将其公开。

          替代方法是将derived::data_ 设为静态成员。

          【讨论】:

          猜你喜欢
          • 2021-09-09
          • 1970-01-01
          • 1970-01-01
          • 2015-01-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-08-02
          • 1970-01-01
          相关资源
          最近更新 更多