为什么内联用户提供的构造函数使用基类构造函数?

2024-02-23

考虑以下说明性示例

#include <iostream>

template <typename>
struct Base {
    static int const touch;
    Base() {
        (void)touch;
    }
};

template<typename CRTP>
int const Base<CRTP>::touch = []{
    std::cout << "Initialized!\n";
    return 0;
}();

struct A : Base<A> {
    A() {} 
};

struct B : Base<B> {
    B() = default;
};

int main() {
}

当上面的程序被编译时GCC https://wandbox.org/permlink/7l6RScgFkvbPeCZY, Clang https://wandbox.org/permlink/n0c7Nj7N2sRMPTq6 or VC++ https://rextester.com/VIFKJ50959并执行后,人们始终会看到以下输出:

Initialized!

所有三个编译器都会发出以下定义和初始化Base<A>::touch,而两者都没有发出定义和初始化Base<B>::touch(也通过 godbolt 验证)。所以我得出的结论是这是标准的制裁行为。

对于默认构造函数B, 我们有

[班级.ctor]

7 https://timsong-cpp.github.io/cppwp/n4659/class.ctor#7当默认构造函数被默认且未定义为已删除时,当它被 odr 用于创建其类类型 ([intro.object]) 的对象时,或者当它在第一次声明后被显式默认时,它会被隐式定义。

由此可以得出结论,由于这两种条件都不适用于我们的 TU,B::B()从来没有隐式定义。所以它永远不会使用Base<B>::Base() and Base<B>::touch。我觉得这很合理。

但是,我不明白为什么A::A()最终使用其基类的成员进行 ODR。我们知道

[类.mfct]

1 https://timsong-cpp.github.io/cppwp/n4659/class.mfct#1成员函数可以在其类定义中定义,在这种情况下,它是内联成员函数...

[dcl.内联]

6 https://timsong-cpp.github.io/cppwp/n4659/dcl.inline#6内联函数或变量应在使用 odr 的每个翻译单元中定义,并且在每种情况下都应具有完全相同的定义 ([basic.def.odr])。

[基本.def.odr]

4 https://timsong-cpp.github.io/cppwp/n4659/basic.def.odr#6...类的构造函数按照 [dcl.init] 中指定的方式使用。

我们从不初始化任何类型的对象A,所以我们不应该使用它的构造函数。所以我们的程序最终不会包含任何定义A::A().

那么为什么它的表现就像存在定义一样呢?为什么要使用odrBase<A>::Base()并导致其实例化?


这发生在anyodr 使用的内联定义Base<T>::Base, 例如:

inline void x() {
    Base<char>();
}

struct A : Base<A> {
    A(int) {}
    void x() {
        Base<int>();
    }
};

struct C : Base<C> {
    C();
};

inline C::C() = default;  // See [1]

// Will print "Initialized!" four times

B() = default不使用 ODRBase<T>::Base因为它仅被定义为违约 https://timsong-cpp.github.io/cppwp/n4659/basic#def-2。尽管在结构上是根据[基本.def]/2 https://timsong-cpp.github.io/cppwp/n4659/basic#def-2,由于 [class.ctor]/7 (如您引用的),它不会“发出”。 odr-use 的定义Base<T>::Base仅当且当时才会(隐式)定义B::B()本身已被 odr 使用。

对于定义的其他内联函数,不存在这样的豁免。它们的定义无条件(和结构上)包含 odr-useBase<T>::Base. A::A()在你的例子中是一个定义,你的程序does包含一个定义A::A()使用Base<T>::Base.

“内联函数或变量应在使用 odr 的每个翻译单元中定义”。这是一个“应”要求(对于每个使用 odr 方式使用内联函数的 TU,都需要定义),而不是使用 odr 方式使用内联函数的结果。


[1] 由于[dcl.fct.def.默认]/5 https://timsong-cpp.github.io/cppwp/n4659/dcl.fct.def#default-5:

用户提供的显式默认函数(即在第一次声明后显式默认)在显式默认的位置定义

So C::C()有一个默认定义,它立即生成“隐式”定义。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么内联用户提供的构造函数使用基类构造函数? 的相关文章

  • C# 静态类型不能用作参数

    public static void SendEmail String from String To String Subject String HTML String AttachmentPath null String Attachme
  • 与 for_each 或 std::transform 一起使用时,如何调用 C++ 函子构造函数

    我以前从未使用过 C 函子 所以我只是想了解它们是如何工作的 例如假设我们有这个函子类 class MultiplyBy private int factor public MultiplyBy int x factor x int ope
  • 静态构造函数和 BeforeFieldInit?

    如果类型没有静态构造函数 则将执行字段初始值设定项 就在使用该类型之前 或者在某个时间点突发奇想 运行时 为什么这段代码 void Main start Dump Test EchoAndReturn Hello end Dump clas
  • 捕获 .aspx 和 .ascx 页面中的异常

    问题说明了一切 请看以下示例代码 ul li li ul
  • EntityHydrate 任务失败

    我最近安装了 Visual Studio 11 Beta 和 Visual Studio 2010 之后 我无法在 Visual Studio 2010 中构建依赖于 PostSharp 的项目 因此我卸载了 Visual Studio 1
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 2个对象,完全相同(除了命名空间)c#

    我正在使用第三方的一组网络服务 但遇到了一个小障碍 在我手动创建将每个属性从源复制到目标的方法之前 我想我应该在这里寻求更好的解决方案 我有 2 个对象 一个是 Customer CustomerParty 类型 另一个是 Appointm
  • 用于在标头更改时重新编译的简单 C 项目的示例 makefile

    有谁有完整的 makefile 可以执行以下操作 如果 HEADER 文件发生更改 则重建项目 cpp 文件在 makefile 中列出 头文件未在 makefile 中列出 头文件允许与 cpp 文件具有不同的名称 部分cpp文件没有头文
  • 条件类型定义

    如果我有一小段这样的代码 template
  • 我们可以通过指针来改变const定义的对象的值吗?

    include
  • .NET 和 Mono 之间的开发差异

    我正在研究 Mono 和 NET C 将来当项目开发时我们需要在 Linux 服务器上运行代码 此时我一直在研究 ASP NET MVC 和 Mono 我运行 Ubuntu 发行版 想要开发 Web 应用程序 其他一些开发人员使用 Wind
  • 以编程方式创建 Blob 存储容器

    我有一个要求 即在创建公司时 在我的 storageaccount 中创建关联的 blob 存储容器 并将容器名称设置为传入的字符串变量 我已尝试以下操作 public void AddCompanyStorage string subDo
  • 使用 gcc 时在头文件中查找定义的好方法是什么?

    在使用 gcc 时 有人有推荐的方法在头文件中查找定义吗 使用 MSVC 时 我只需右键单击并选择 转到定义 这非常好 我使用过 netbeans gcc 它确实有代码帮助 包括到定义的超链接 所以这是一种选择 但是 我想知道是否有任何其他
  • 如何在C#中控制datagridview光标移动

    我希望 datagridview 光标向右移动到下一列 而不是在向单元格输入数据后移动到下一行 我试图通过 dataGridView1 KeyDown 事件捕获键来控制光标 但这并不能阻止光标在将数据输入到单元格后移动到下一行 提前感谢你的
  • 如果将变量设置为等于新对象,旧对象会发生什么?

    假设我们有一个 X 类not有一个超载的operator 功能 class X int n X n 0 X int n n n int main X a 1 an object gets constructed here more code
  • winform c# 中的弹出窗口

    我正在开发一个需要弹出窗口的项目 但问题是我还希望能够通过表单设计器在此弹出窗口中添加文本框等 所以基本上我有一个按钮 当您单击它时 它将打开我在表单设计器中设计的另一个窗口 我一直在谷歌搜索 但还没有找到我需要的东西 所以我希望你们能帮助
  • .Net Reactive Extensions Framework (Rx) 是否考虑拓扑顺序?

    Net 反应式扩展框架是否按拓扑顺序传播通知以最大限度地减少更新量 就像 Scala Rx 所做的那样 Net 反应式扩展 Rx 是否可以 https github com lihaoyi scala rx wiki How it Work
  • 从后面的代码添加外部 css 文件

    我有一个 CSS 文件 例如 SomeStyle css 我是否可以将此样式表文档从其代码隐藏应用到 aspx 页面 您可以将文字控件添加到标头控件中 Page Header Controls Add new System Web UI L
  • 声明一个负长度的数组

    当创建负长度数组时 C 中会发生什么 例如 int n 35 int testArray n for int i 0 i lt 10 i testArray i i 1 这段代码将编译 并且启用 Wall 时不会出现警告 并且似乎您可以分配
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐