考虑以下类:
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
型正确对齐和大小的存储,以及如果对象具有非空初始化,则其初始化完成,
因此,必须执行初始化。然后,我们查看所有可能的初始化。
-
如果初始值设定项
{...}
,则它是列表初始化的。- 该类是非聚合的,因此不能进行聚合初始化。
- 该类没有默认构造函数,因此不能通过
{}
对其进行值初始化 - 该类可以通过
{x}
直接初始化或复制初始化,通过重载解析调用构造函数
-
如果初始值设定项
()
,则它是值初始化的 -
如果初始值设定项是相同的 cv 非限定类型,则使用初始值设定项的初始值设定项表达式作为初始值设定项
- 也就是说,
T x = T(T());
只是默认初始化x
,没有副本
- 也就是说,
-
如果没有初始值设定项,则默认初始化
-
否则,将考虑并使用重载解析来调用构造函数。
-
在这种情况下,值初始化就是默认初始化对象。
-
在这种情况下,默认初始化调用默认构造函数。
如果仔细查看,此列表中只有一种方法可以初始化对象,那就是调用构造函数。由于我们已经删除了所有构造函数,因此无法初始化对象。
我不声称这是一个答案(我不是语言律师),只是发布来自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();
}