代码之家  ›  专栏  ›  技术社区  ›  Vincent

是否可以实例化具有已删除构造函数和析构函数的非聚合类?

  •  10
  • Vincent  · 技术社区  · 6 年前

    考虑以下类别:

    template <class T>
    class object {
    public:
        using type = T;
    
        object() = delete;
        object(const object&) = delete;
        object(object&&) = delete;
        object& operator=(const object&) = delete;
        object& operator=(object&&) = delete;
        ~object() = delete;
    
    private:
        type value = 0;
    };
    

    是否可以实例化一个对象(在堆栈上(可能没有)或在堆上(可能?)的类型 object<int> 通过任何诡计,还是不可能 对象(<);内部(>); 以任何可以想象的方式存在(形式良好,行为明确) C++17 程序

    附录:首先,我们考虑非病态NDR程序/非未定义行为。如果且仅当可能存在格式错误的NDR程序/未定义的行为程序时,您可以用这样的程序来说明答案。

    3 回复  |  直到 6 年前
        1
  •  4
  •   Community pid    4 年前

    不,它不能。从…起 [basic.life]

    […]类型的对象的生存期 T 开始时间:

    • 存放时应保持适当的对齐和尺寸 T 获得,以及

    • 如果对象具有非真空初始化,则其初始化完成,

    因此,必须执行初始化。然后我们看看 all the possible initializations

    1. 如果初始值设定项为 {...} ,那么就是了 list-initialized

      • 类是非聚合的,因此不能 aggregate-initialized
      • 该类没有默认构造函数,因此不能由值初始化 {}
      • 该类可以由直接初始化或复制初始化 {x} ,它通过重载解析调用构造函数
    2. 如果初始值设定项为 () ,则为值初始化

    3. 如果初始值设定项是相同的cv非限定类型,则使用初始值设定项的初始值设定项表达式作为初始值设定项

      • 也就是说, T x = T(T()); 只是默认初始化 x ,无副本
    4. 如果没有初始值设定项,则默认为已初始化

    5. 否则,将考虑使用重载解析调用构造函数。

    6. 在这种情况下,值初始化是默认初始化对象。

    7. 在这种情况下,默认初始化调用默认构造函数。

    如果仔细观察,在这个列表中只有一种方法可以初始化对象,那就是调用构造函数。由于已删除所有构造函数,因此无法初始化对象。

        2
  •  1
  •   Daniel Langr    6 年前

    我并不声称这是一个答案(我不是语言律师),只需发布n4659的一些发现:

    11.6.20:初始化已完成的对象被视为已构造,即使没有调用objects类的构造函数进行初始化。[注: 此类对象可能已初始化值 或 通过聚合初始化(11.6.1)或继承的构造函数(15.6.3)初始化。–结束注释]

    在注释中提到的3个选项中,只有第一个选项适用:

    11.6.8:至 值初始化 类型为T的对象表示。。。(8.1)–如果T是(可能符合cv条件的)类类型(第12条),没有默认构造函数(15.1) 或默认构造函数 用户提供或 已删除 ,则对象为 默认已初始化 ;

    由于删除了默认构造函数:

    11.6.7:至 默认初始化 类型为T的对象表示。。。(7.1)如果T是一种(可能是cv合格的)等级类型(第12条),则应考虑施工人员。列举了适用的构造函数(16.3.1.3),并通过重载解析(16.3)选择了初始值设定项()的最佳构造函数。 这样选择的构造函数被调用 ,使用空参数列表初始化对象。

    由于没有可以调用的构造函数,我认为类的实例 object 无法构造。

        3
  •  -1
  •   Robert Andrzejuk    6 年前

    规则很明确,无法正确初始化此对象。

    既然这就是所要求的,那么就为类创建一个缓冲区,并小心使用它。

    template< typename T >
    class object_wrapper
    {
        std::byte buffer[ sizeof( T ) ];
    
    public:
        auto& inner() { return *reinterpret_cast< T* >( buffer ); }
    };
    

    以下是一个示例:

    int main()
    {
        //object< int > o; // impossible
    
        object_wrapper< object< int > > ow; // wrap the object
    
        object< int >& o( ow.inner() );     // possible
    
        o.some_foo();
    
    }