C++ 仅通过指针转换生成可互换的类类型,而不必分配任何新对象?

C++ 仅通过指针转换生成可互换的类类型,而不必分配任何新对象?,c++,c++11,language-lawyer,reinterpret-cast,type-punning,C++,C++11,Language Lawyer,Reinterpret Cast,Type Punning,更新:我非常感谢“不要那样,而是要这样”的建议。它们非常有用,特别是在上下文中提供时。尽管如此……不管好坏,我还是很好奇地找到一个硬性的答案“是的,在C++11中可以合法地完成”,而不是“不,这样做是不可能的” 我想将对象指针“别名”为另一种类型,唯一的目的是添加一些帮助器方法。别名无法将数据成员添加到基础类中(事实上,我越能防止这种情况发生越好!)所有别名都同样适用于此类型的任何对象……如果类型系统能够提示哪个别名可能最合适,这将非常有帮助 底层对象中不应该有任何特定别名的编码信息。因此,我

更新:我非常感谢“不要那样,而是要这样”的建议。它们非常有用,特别是在上下文中提供时。尽管如此……不管好坏,我还是很好奇地找到一个硬性的答案“是的,在C++11中可以合法地完成”,而不是“不,这样做是不可能的”


我想将对象指针“别名”为另一种类型,唯一的目的是添加一些帮助器方法。别名无法将数据成员添加到基础类中(事实上,我越能防止这种情况发生越好!)所有别名都同样适用于此类型的任何对象……如果类型系统能够提示哪个别名可能最合适,这将非常有帮助

底层对象中不应该有任何特定别名的编码信息。因此,我觉得您应该能够“欺骗”类型系统,让它成为一个注释……在编译时检查,但最终与运行时强制转换无关。大致如下:

Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);
但如果我不必为这些付出,我也不想。例如,这将涉及为每种包装指针(AccessorFooShared、AccessorFooUnique、AccessorFooWeak等)创建一个特殊的访问器类型,最好将这些类型化的指针作为基于单个指针的对象标识的别名,并提供良好的正交性

回到原来的问题:

Node<AccessorFoo>* fooPtr = Node<AccessorFoo>::createViaFactory();
Node<AccessorBar>* barPtr = reinterpret_cast< Node<AccessorBar>* >(fooPtr);
节点:

对象指针可以显式转换为不同类型的对象指针。70当类型为“指向T1的指针”的PRV值转换为类型为“指向cv T2的指针”时,如果T1和T2都是标准布局类型(3.9),并且T2的对齐要求不比T1严格,则结果为静态_cast(静态_cast(v)),或者如果其中一种类型为void。将“指针指向T1”类型的PR值转换为“指针指向T2”类型(其中T1和T2是对象类型,T2的对齐要求不比T1严格),然后返回到其原始类型,即可生成原始指针值。未指定任何其他此类指针转换的结果

深入研究a的定义,我们发现:

  • 没有非标准布局类(或此类类型的数组)或引用类型的非静态数据成员,以及
  • 没有虚拟函数(10.3)和虚拟基类(10.1),并且
  • 对所有非静态数据成员具有相同的访问控制(第11条),以及
  • 没有非标准布局基类,并且
  • 在最派生的类中没有非静态数据成员,并且最多有一个基类具有非静态数据成员,或者没有基类具有非静态数据成员,以及
  • 没有与第一个非静态数据成员类型相同的基类
听起来,如果在访问器或节点中没有虚拟方法,那么使用这样的东西会有点束缚我的手脚。然而C++11显然有
std::is_标准_布局
来保持检查


这样做安全吗?似乎在gcc-4.7中工作,但我想确定我没有调用未定义的行为。

如果我理解正确,您有:

  • 一个有状态的
    节点库
    类,是系统真正的主力军
  • 一组无状态的
    访问器
    类型,为
    节点库
    提供接口;及
  • 一个
    节点


    <> P>引用的C++标准的部分,顺便提及,在源类型和最终类型都是指向标准布局对象的情况下,代码< > RealTytCaseP>(p)<代码>的行为,在这种情况下,标准保证您得到的指针与您从转换到
    void*
    然后到最终类型时得到的指针相同。在不调用未定义的行为的情况下,您仍然无法将该对象作为创建它时所使用的类型以外的任何类型使用。

    术语访问器是一个死赠品:您正在寻找的是一个

    没有理由不通过值传递代理

    // Let us imagine that NodeBase is now called Node, since there is no inheritance
    
    class AccessorFoo {
    public:
        AccessorFoo(Node& n): node(n) {}
    
        int bar() const { return node->bar; }
    
    private:
        std::reference_wrapper<Node> node;
    };
    
    //假设NodeBase现在被称为Node,因为没有继承
    类访问器foo{
    公众:
    AccessorFoo(Node&n):Node(n){}
    int bar()常量{return node->bar;}
    私人:
    std::reference_包装节点;
    };
    

    然后你可以自由地从一个访问器转换到另一个。。。虽然这有味道。通常情况下,拥有一个访问器的目的是以某种方式限制访问,因此将nilly-willy强制转换到另一个访问器是不好的。但是,您可以支持对较窄的访问器进行强制转换。

    我相信严格的别名规则禁止您尝试执行的操作

    澄清一下:严格的别名与布局兼容性、POD类型等无关。这与优化有关。语言明确禁止你做的事

    本文对它进行了很好的总结:

    structmypod{
    int data1;
    // ...
    };
    结构myPOD_扩展1:myPOD{
    int helper(){(*(myPOD*)this)->data1=6;};//键入此处引用的myPOD
    };
    结构myPOD_扩展2:myPOD{
    int helper(){data1=7;};//没有对myPOD的语法引用
    };
    结构myPOD_扩展3:myPOD{
    int helper(){(*(myPOD*)this)->data1=8;};//键入此处引用的myPOD
    };
    void doit(myPOD*it)
    {
    ((myPOD_extended1*)it)->helper();
    //((myPOD_extended2*)it)->helper();//取消注释->程序/编译未定义
    ((myPOD_extended3*)it)->helper();
    }
    int main(int,char**)
    {
    myPOD a;a.data1=5;
    doit&a;
    
    std::cout初始语法应该是
    Node&foo=*new Node;
    。不过我还是不明白这个问题:)@iammilind-Oops,打字错误,修复了…谢谢!原则上它可能非常类似,我只是对涉及的类型有更多的控制权,可以从头重写所有内容
    struct AccessorFoo {
        int getValue(NodeBase* n) { return n->getValueFoo(); }
    };
    
    struct AccessorBar {
        int getValue(NodeBase* n) { return n->getValueBar(); }
    };
    
    template <typename AccessorT>
    class Node {
        NodeBase* m_node;
    
    public:
        int getValue() {
            AccessorT accessor;
            return accessor.getValue(m_node);
        }
    };
    
    template <typename OtherT>
    operator Node<OtherT>() {
        return Node<OtherT>(m_node);
    }
    
    // Let us imagine that NodeBase is now called Node, since there is no inheritance
    
    class AccessorFoo {
    public:
        AccessorFoo(Node& n): node(n) {}
    
        int bar() const { return node->bar; }
    
    private:
        std::reference_wrapper<Node> node;
    };
    
    struct myPOD {
       int data1;
       // ...
    };
    
    struct myPOD_extended1 : myPOD {
       int helper() { (*(myPOD*)this)->data1 = 6; };  // type myPOD referenced here
    };
    struct myPOD_extended2 : myPOD { 
       int helper() { data1 = 7; };                   // no syntactic ref to myPOD
    };
    struct myPOD_extended3 : myPOD {
       int helper() { (*(myPOD*)this)->data1 = 8; };  // type myPOD referenced here
    };
    void doit(myPOD *it)
    {
        ((myPOD_extended1*)it)->helper();
        // ((myPOD_extended2*)it)->helper(); // uncomment -> program/compile undefined
        ((myPOD_extended3*)it)->helper();
    }
    
    int main(int,char**)
    {
        myPOD a; a.data1=5; 
        doit(&a);
    
        std::cout<< a.data1 << '\n';
        return 0;
    }