Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在C+;中是否有一种方法可以同时将一个类型分配给多个模板+;?_C++_Templates_Generic Programming_Type Erasure - Fatal编程技术网

C++ 在C+;中是否有一种方法可以同时将一个类型分配给多个模板+;?

C++ 在C+;中是否有一种方法可以同时将一个类型分配给多个模板+;?,c++,templates,generic-programming,type-erasure,C++,Templates,Generic Programming,Type Erasure,这个问题基于下面的示例代码,其灵感来自。 下面代码的目标是提供类似于的对象包装器。我写这段代码是为了自学类型擦除。因此,这段代码没有实际用途(考虑到已经有boost::any) 这是可行的,但很明显,getObjPtr(b)没有按预期工作 所以,我的问题是: 有没有办法修复上面的代码,这样我们就可以简单地使用int*p_a=getObjPtr(a)和std::string*p_b=getObjPtr(b)或者更好的auto p_a=getObjPtr(a)和auto p_b=getObjPtr

这个问题基于下面的示例代码,其灵感来自。 下面代码的目标是提供类似于的对象包装器。我写这段代码是为了自学类型擦除。因此,这段代码没有实际用途(考虑到已经有boost::any)

这是可行的,但很明显,
getObjPtr(b)
没有按预期工作

所以,我的问题是:

有没有办法修复上面的代码,这样我们就可以简单地使用
int*p_a=getObjPtr(a)
std::string*p_b=getObjPtr(b)
或者更好的
auto p_a=getObjPtr(a)
auto p_b=getObjPtr(b)
?换言之,C++中是否有一种方法同时对两个模板进行实例化(如果是这样的话,我们可以在<代码> Objult< /Cord>对象,例如<代码> ObjRead(1)< /C> > /< P>编译时,实例化<代码> Objult< /Cuff>构造函数和<代码> T*GETObjpTR(ObjRead)< /C>。 编辑1:

使ObjWrap成为模板化类并没有帮助,因为这样做会破坏类型擦除的目的

template <typename T>
class ObjWrap {
  /* ... */
};

ObjWrap<int> a(1); // this is no good for type erasure. 
模板
类ObjWrap{
/* ... */
};
ObjWrap a(1);//这不利于类型擦除。
编辑2:

我正在阅读代码,并意识到可以对其进行修改,以更好地反映这个想法。因此,请同时查看以下代码:

class ObjWrap {
public:
  template <typename T>
  ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}

  template <typename T>
  T * getObjPtr() {
    return static_cast<T*>(Self->getObjPtr_());
  }
private:
  struct Concept {
    virtual ~Concept() = 0;
    virtual void* getObjPtr_() = 0;
  };
  template <typename T>
  struct Obj : Concept {
    Obj(T O) : Data(std::move(O)) {}
    void* getObjPtr_() { return static_cast<void*>(&Data); }

    T Data;
  };

  std::unique_ptr<Concept> Self;
};

int main() {
  ObjWrap a(1);
  ObjWrap b(std::string("b"));
  int* p_a = a.getObjPtr<int>();
  std::string* p_b = b.getObjPtr<std::string>();

  std::cout << *p_a << " " << *p_b << "\n";

  return 0;
}
类ObjWrap{
公众:
模板
ObjWrap(to):Self(新的Obj(std::move(O)){}
模板
T*getObjPtr(){
返回静态_cast(Self->getObjPtr_());
}
私人:
结构概念{
虚~概念()=0;
虚拟void*getObjPtr_u3;=0;
};
模板
结构对象:概念{
Obj(to):数据(std::move(O)){
void*getObjPtr_u2;(){return static_cast(&Data);}
T数据;
};
std::唯一的自身;
};
int main(){
obja(1);
ObjWrap b(标准::字符串(“b”);
int*p_a=a.getObjPtr();
std::string*p_b=b.getObjPtr();
标准::cout
  • (等)如果这是错误的,请纠正我

  • 你的前提至少在原则上是错误的,如果不是在实践中也是错误的。你坚持让
    getObjPtr()
    成为一个虚拟方法,并使用一个抽象基类。但是-你还没有建立这一点是必要的。记住-使用虚拟方法是昂贵的!我为什么要为获得类型擦除而支付虚拟方法的费用

    有没有办法修复上面的代码,这样我们就可以简单地使用
    int*p_a=getObjPtr(a)

    牢记Sean Parent的演讲标题(与他在演讲中使用继承的事实相反),删除继承,答案应该是肯定的。编辑:擦除类型的代码和取消擦除类型的代码就足以知道类型是什么-只要您不需要以特定于类型的方式对类型擦除的数据执行操作。在Sean Parent的谈话中,您需要能够制作它的非平凡副本,不是吗o移动它,绘制它等等。使用
    std::any
    /
    boost::any
    您可能需要复制和移动,这可能需要虚拟化,但这是最常见的用例

    即使是
    std::any
    也会限制您可以做和不能做的事情,如本问题所述:


    有几件事可能会有所帮助


    首先要说的是,如果Obj需要公开对象的地址,那不是Sean Parent的“继承是万恶之源”类型擦除容器

    诀窍是确保Obj的接口提供包装器所需的所有语义操作和查询

    为了提供这一点,在概念中缓存对象的地址及其
    type_id
    通常是一个合理的想法

    考虑以下更新的示例,其中有一个公共方法-operator==。规则是,如果两个对象包含相同类型的对象,并且这些对象比较相等,则它们相等

    请注意,地址和类型\u id:

    1) 是实现细节,未在Obj接口上公开

    2) 无需虚拟调用即可访问,从而缩短了不平等情况

    #include <memory>
    #include <utility>
    #include <typeinfo>
    #include <utility>
    #include <cassert>
    #include <iostream>
    
    class ObjWrap 
    {
    public:
        template <typename T>
        ObjWrap(T O) : Self(new Model<T>(std::move(O))) {}
    
        // objects are equal if they contain the same type of model
        // and the models compare equal
        bool operator==(ObjWrap const& other) const
        {
            // note the short-circuit when the types are not the same
            // this means is_equal can guarantee that the address can be cast
            // without a further check
            return Self->info == other.Self->info
            && Self->is_equal(other.Self->addr);
        }
    
        bool operator!=(ObjWrap const& other) const
        {
            return !(*this == other);
        }
    
        friend std::ostream& operator<<(std::ostream& os, ObjWrap const& o)
        {
            return o.Self->emit(os);
        }
    
    private:
        struct Concept 
        {
            // cache the address and type here in the concept.
            void* addr;
            std::type_info const& info;
    
            Concept(void* address, std::type_info const& info)
            : addr(address)
            , info(info)
            {}
    
            virtual ~Concept() = default;
    
            // this is the concept's interface    
            virtual bool is_equal(void const* other_address) const = 0;
            virtual std::ostream& emit(std::ostream& os) const = 0;
        };
    
        template <typename T>
        struct Model : Concept 
        {
            Model(T O) 
            : Concept(std::addressof(Data), typeid(T))
            , Data(std::move(O)) {}
    
            // no need to check the pointer before casting it.
            // Obj takes care of that
            /// @pre other_address is a valid pointer to a T    
            bool is_equal(void const* other_address) const override
            {
                return Data == *(static_cast<T const*>(other_address));
            }
    
            std::ostream& emit(std::ostream& os) const override
            {
                return os << Data;
            }
    
            T Data;
        };
    
    
    std::unique_ptr<Concept> Self;
    };
    
    
    int main()
    {
        auto x = ObjWrap(std::string("foo"));
        auto y = ObjWrap(std::string("foo"));
        auto z = ObjWrap(int(2));
    
        assert(x == y);
        assert(y != z);
    
        std::cout << x << " " << y << " " << z << std::endl;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    类ObjWrap
    {
    公众:
    模板
    ObjWrap(to):Self(新模型(std::move(O)){}
    //如果对象包含相同类型的模型,则它们是相等的
    //模型之间的比较结果相同
    布尔运算符==(ObjWrap常量和其他)常量
    {
    //注意类型不同时的短路
    //这意味着is_equal可以保证地址可以被强制转换
    //没有进一步检查
    返回Self->info==other.Self->info
    &&Self->is_equal(其他.Self->addr);
    }
    布尔运算符!=(ObjWrap常量和其他)常量
    {
    返回!(*此==其他);
    }
    
    friend std::ostream&Operator但是Sean Parent在演讲中介绍的代码也使用虚拟方法…boost::any也使用虚拟方法…“为什么我应该为获得类型擦除而付费?”如何在没有虚拟函数的情况下实现类型擦除?当然,您可以使用函数指针,但之后您可以有效地重新实现虚拟函数。“使用虚拟方法是昂贵的!”使用虚拟方法是有代价的。是否“昂贵”取决于情况。@πάνταῥεῖ 我正要发布一个回答OP真正的问题的答案。请重新打开。你不应该在重新阅读的基础上向一个问题添加越来越多的代码。这不是该网站应该使用的方式。@einpoklum对此表示抱歉。我想这可能没问题,因为它本质上是相同的代码,在网上看到它们可能会很有趣页面。“首先要说的是,如果Obj需要公开对象的地址,那不是Sean Parent的‘继承是万恶之源’类型擦除容器。”感谢你指出这一点。你是对的。我的代码试图表现得像boost::any,而我是w
    class ObjWrap {
    public:
      template <typename T>
      ObjWrap(T O) : Self(new Obj<T>(std::move(O))) {}
    
      template <typename T>
      T * getObjPtr() {
        return static_cast<T*>(Self->getObjPtr_());
      }
    private:
      struct Concept {
        virtual ~Concept() = 0;
        virtual void* getObjPtr_() = 0;
      };
      template <typename T>
      struct Obj : Concept {
        Obj(T O) : Data(std::move(O)) {}
        void* getObjPtr_() { return static_cast<void*>(&Data); }
    
        T Data;
      };
    
      std::unique_ptr<Concept> Self;
    };
    
    int main() {
      ObjWrap a(1);
      ObjWrap b(std::string("b"));
      int* p_a = a.getObjPtr<int>();
      std::string* p_b = b.getObjPtr<std::string>();
    
      std::cout << *p_a << " " << *p_b << "\n";
    
      return 0;
    }
    
    #include <memory>
    #include <utility>
    #include <typeinfo>
    #include <utility>
    #include <cassert>
    #include <iostream>
    
    class ObjWrap 
    {
    public:
        template <typename T>
        ObjWrap(T O) : Self(new Model<T>(std::move(O))) {}
    
        // objects are equal if they contain the same type of model
        // and the models compare equal
        bool operator==(ObjWrap const& other) const
        {
            // note the short-circuit when the types are not the same
            // this means is_equal can guarantee that the address can be cast
            // without a further check
            return Self->info == other.Self->info
            && Self->is_equal(other.Self->addr);
        }
    
        bool operator!=(ObjWrap const& other) const
        {
            return !(*this == other);
        }
    
        friend std::ostream& operator<<(std::ostream& os, ObjWrap const& o)
        {
            return o.Self->emit(os);
        }
    
    private:
        struct Concept 
        {
            // cache the address and type here in the concept.
            void* addr;
            std::type_info const& info;
    
            Concept(void* address, std::type_info const& info)
            : addr(address)
            , info(info)
            {}
    
            virtual ~Concept() = default;
    
            // this is the concept's interface    
            virtual bool is_equal(void const* other_address) const = 0;
            virtual std::ostream& emit(std::ostream& os) const = 0;
        };
    
        template <typename T>
        struct Model : Concept 
        {
            Model(T O) 
            : Concept(std::addressof(Data), typeid(T))
            , Data(std::move(O)) {}
    
            // no need to check the pointer before casting it.
            // Obj takes care of that
            /// @pre other_address is a valid pointer to a T    
            bool is_equal(void const* other_address) const override
            {
                return Data == *(static_cast<T const*>(other_address));
            }
    
            std::ostream& emit(std::ostream& os) const override
            {
                return os << Data;
            }
    
            T Data;
        };
    
    
    std::unique_ptr<Concept> Self;
    };
    
    
    int main()
    {
        auto x = ObjWrap(std::string("foo"));
        auto y = ObjWrap(std::string("foo"));
        auto z = ObjWrap(int(2));
    
        assert(x == y);
        assert(y != z);
    
        std::cout << x << " " << y << " " << z << std::endl;
    }