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

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 中的隐秘结构定义

    我遇到了以下情况迷宫定义 https github com gduarte lkb blob master code stack maze h code typedef struct mazeNode int hasCheese int t
  • 什么是具有副作用的表达式?为什么不应将它们传递给宏?

    我在 C 如何编程 一书中看到这样一句话 具有副作用 即变量值被修改 的表达式不应传递给宏 因为宏参数可能会被多次求值 我的问题是什么是具有副作用的表达式以及为什么不应将它们传递给宏 经典的例子是计算两个值的最大值的宏 define MAX
  • Qt:更改 Mac OS X 上的应用程序 QMenuBar 内容

    我的应用程序对多个 页面 使用 QTabWidget 其中顶级菜单根据用户所在的页面而变化 我的问题是 尝试重新创建菜单栏的内容会导致严重的显示问题 它在除 Mac OS X 之外的所有平台上按预期使用第一种和第三种样式 尚未测试第二种 但
  • tmpnam 的 C/C++ 线程安全性?

    我需要使用tmpnamC 中的函数 但我需要了解它的线程安全性 也就是说 如果我有多个线程 每个线程都需要为临时文件获取不同的名称 我是否可以保证每个线程都会收到具有不同名称的文件 tmpnam 仅保证该文件当时不存在 但它可能会在您自己创
  • 如何使用 LINQ 对列表的列表进行分组(例如:List>)

    我知道我可以使用一些 for 循环轻松地做到这一点 但想看看是否有一种方法可以使用流畅的 LINQ 来做到这一点 我试图找出每个子列表中有多少个 我在看Enumerable SequenceEqual http msdn microsoft
  • 如果将其名称作为参数传递,如何在方法中打开表单

    我正在尝试创建一个标准方法来根据传递给它的参数打开表单 基本上 要完成此任务 using Quotes newQte new Quotes newQte ShowDialog 通过替换 Quotes with a passed parame
  • esp8266互联网交换机问题

    我正在尝试制作一个门继电器开关系统 我可以通过端口转发从任何地方进行操作 我找到了一个非常有用的指南和代码 我的程序基于 https openhomeautomation net control a lamp remotely using
  • 如何在提升日期时间中忽略周末和节假日?

    第一个问题 我有一个提升日期对象 如下所示 boost gregorian date 今天 2012 02 13 我从今天减去日期部分 如下所示 今天 月 240 或今天 天 X 等 我想在进行上述减法时是否有办法排除周末和特殊假期 我的意
  • 将图像添加到 ASP.Net 中的单选按钮列表

    我正在尝试将图像添加到单选按钮列表控件 但它不起作用 我试过这个 RadioButtonList2 Items Add new ListItem String Format src Colors Dallas 625527 1 1 png
  • 找到两个值的平均值的正确方法是什么?

    我最近了解到整数溢出是 C 中的未定义行为 附带问题 C 中也是 UB 吗 在 C 编程中 您通常需要求两个值的平均值a and b 然而做 a b 2可能会导致溢出和未定义的行为 所以我的问题是 找到两个值的平均值的正确方法是什么a an
  • if(pointerVar) 与 if(pointerVar!=NULL) 相同吗?

    简单的问题 Is if pointerVar 与if pointerVar NULL 也是if pointerVar 与if pointerVar NULL 给我你在技术上最正确 迂腐的答案 这两种说法看起来和操作起来都是一样的 前者有什么
  • 寻找自定义 SynchronizationContext 的示例(单元测试所需)

    我需要定制同步上下文 http msdn microsoft com en us library system threading synchronizationcontext aspx that 拥有一个运行 Posts 和 Sends
  • C# 从字符串变量中获取类型并在泛型方法中使用它

    我希望能够通过某种方式 即从数据库 获取我收到的字符串值的实际类型 这样我就可以在通用方法中使用该类型 例如DoSomething
  • Web Api 2 在 OWIN 中间件中获取控制器和操作名称?

    如何在自定义 OWIN 中间件中检索 api 控制器名称和 api 操作名称 我可以在消息处理程序内部执行此操作 如下所示 var config request GetConfiguration var routeData config R
  • MonoMac 窗口关闭时没有错误

    我刚刚开始在 Xamarin Studio 中使用 MonoMac 并且遇到了最奇怪的问题 我有一个带有 NSButton 和 NSTextField 的窗口 至此 我已经删除了按钮上的事件处理程序 因此它不会执行任何操作 除了在单击它时突
  • Windows 中的蓝牙 AVRCP 命令会触发哪些事件

    可以这么说 只是在做一些高级侦察 对于我的潘多拉客户 Elpis http elpis adamhaile net 我支持全局媒体键 键盘上的 MediaPlayPause MediaNext 等 并且我希望能够支持AVRCP http e
  • 隐藏 MediaPlayer 控件(Microsoft 媒体平台播放器框架)

    我在 c xaml 应用程序中使用 MMP PF 提供我自己的控制元素来处理播放器 这就是为什么我想隐藏 禁用出现在底部的本机控件 在屏幕截图的屏幕中间 这只是使用了一个主题 有人知道该怎么做吗 我没能找到合适的房产 像这样使用 axWin
  • 我的 Visual Studio 2008 模板有什么问题?

    我正在尝试为 Visual Studio 创建自己的类模板 称为 公共类 我跟着有关如何手动创建项目模板的官方 MSDN 说明 http msdn microsoft com en us library ms247113 aspx几乎一字不
  • 如何将curlpp 添加到我的项目中?

    我正在尝试从 vb net 过渡到 C 但我陷入了困境 我从下载了curpp这给了我一个 dll exp 和 lib 文件 我将包含这 3 个文件的目录添加到项目属性中的 附加库目录 链接器 gt 常规 接下来 我将 ws2 32 lib
  • 复杂对象上的 GroupBy(例如 List

    Using GroupBy and Count gt 1我试图在列表中查找我的类的重复实例 该类看起来像这样 public class SampleObject public string Id public IEnumerable

随机推荐