【问题标题】:Construct an empty object without the default constructor构造一个没有默认构造函数的空对象
【发布时间】:2019-11-22 06:23:41
【问题描述】:

假设我有一个类型F。我知道F是空的,但是F没有默认构造函数,所以我不能用F()来构造它。有没有办法获得F 类型的有效对象?我似乎记得提到过,有一种方法可以神秘地使用联合。理想情况下,它应该是 constexpr 友好的。


这很有用,因为无捕获 lambda 仅在 C++20 中获得了默认构造函数。在 C++17 中,如果我想“将 lambda 传递给模板”并在没有它的实例的情况下调用该 lambda,我需要能够从类型重构它。

auto const f = [](int x) { return x; };
using F = decltype(f);

static_assert(std::is_empty_v<F>);
static_assert(!std::is_default_constructible_v<F>);

magically-construct-an-F(42);

【问题讨论】:

  • "在 C++17 中,如果我想“将 lambda 传递给模板”" 就不要这样做了。只需创建一个带有operator() 重载的struct。打字不多:struct F {auto operator()(int x) {return x;}};
  • 什么模板要默认构造函数对象?您是否要排除实际功能?
  • @Caleth:例如,给std::set/map 的比较函子。它必须是可默认构造的,以便可以用于比较元素。
  • @Caleth 简而言之,对于我的特定用例,这允许您将空对象的成员函数提升为静态成员函数,从而为您节省一个指针参数。在我的特定用例中,编译器无法删除该指针。而且,对于我的特定用例,我找到了一种可能的解决方法。

标签: c++ language-lawyer c++17 empty-class


【解决方案1】:

对于您自己的类型,您可以从自身复制或移动构造一个对象:F f = f。这本身不会导致 UB,请参阅CWG363。 但是,对于编译器提供的闭包类型,即使您知道它是空的,也不是很清楚。

【讨论】:

  • "您可以从自身复制或移动构造一个对象。" 好的,那么...您如何获得“自身”?
  • @NicolBolas MyClass foo(foo); 我猜?
  • 如果你也保证std::is_trivially_copyable_v&lt;F&gt;呢?在这种情况下,复制构造函数应该是无操作的,因此可以保证它可以工作。
  • @Justin:我的意思是它在逻辑上破坏了 C++,正如我在后续句子中解释的那样。语言清楚地认为你应该能够做和/或阻止的事情实际上并不能阻止这些事情。就像x.y 遵循公共/私人规则,但x-&gt;y 没有,让您访问所有内容。 private 在技术上仍然存在并且可以工作,但是让它变得毫无意义是微不足道的。这里也是如此:控制谁来构造你的类型的能力应该存在在 C++ 中。
  • @NicolBolas 我想知道为什么这不算访问 f By the definition of access?
【解决方案2】:

F 没有默认构造函数

如果是这种情况,那么用户要么显式删除它,要么因为用户提供了其他构造函数而被隐式删除。无论哪种情况,类型都不是 Trivial。

如果一个对象是非平凡的,那么要创建该类型的对象而不从现有实例复制/移动,您必须显式调用某种构造函数。没有办法解决这个问题。

即使是联合的常见初始顺序规则也不允许您创建另一个对象。它只允许访问另一个对象的非静态数据成员。由于您的对象是空的,因此这对您没有任何价值。

【讨论】:

  • “即使是联合的常见初始顺序规则也不允许您创建另一个对象” 那么编译器错误报告可能是按顺序排列的。我测试的每个编译器都接受这个代码,它在constexprgodbolt.org/z/RSEfax
  • @Justin:那些编译器有问题。允许这样的事情会彻底破坏 C++ 对象模型。
  • gcc、clang、MSVC、icc 和我测试的所有其他编译器在constexpr 上允许这样做的事实(暗示他们认为这是已定义的行为)表明这里可能会发生更多事情.似乎每个编译器都不太可能有同样的错误。或者有可能每个编译器都允许它作为实现定义
  • @贾斯汀:This is intro.object/1。它指定了对象可以存在的方式。隐式创建联合成员的规则不包括从函数返回命名成员。因此,这不是其中之一。因此,除非您可以显示标准在哪里明确指出这不是 UB,并且该代码以某种方式导致对象出现,否则所有这些编译器都会以这种方式被破坏。此外,如果您能找到这样的事情,请将其作为缺陷报告归档,因为您不应该这样做
  • @Justin:在该示例中,您将什么传递给 f2,以及如何/为什么?
猜你喜欢
  • 1970-01-01
  • 2023-03-20
  • 1970-01-01
  • 2016-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多