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



考虑以下类:

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>的对象(在堆栈上(可能不是)或堆上(也许?)),或者object<int>是否不可能存在于任何可以想象的(格式良好且具有明确定义的行为)C++17程序中?

附录:首先,我们考虑格式不正确的 NDR 程序/未定义的行为。当且仅当只有格式错误的 NDR 程序/未定义的行为程序可能,您可以使用这样的程序来说明答案。

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

[...]类型T对象的生存期在以下情况下开始:

  • 获得具有T型正确对齐和大小的存储,以及

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

因此,必须执行初始化。然后,我们查看所有可能的初始化。

  1. 如果初始值设定项{...},则它是列表初始化的。

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

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

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

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

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

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

如果仔细查看,此列表中只有一种方法可以初始化对象,那就是调用构造函数。由于我们已经删除了所有构造函数,因此无法初始化对象。

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

11.6.20:初始化已完成的对象被视为已构造,即使没有为初始化调用对象类的构造函数也是如此。[ 注意:此类对象可能已初始化值或 通过聚合初始化 (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的实例。

规则很清楚,这个对象不能正确初始化。

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

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();
}

最新更新