【问题标题】:Trouble understanding the C++11 syntax in the Rule of Zero无法理解零规则中的 C++11 语法
【发布时间】:2013-11-12 21:48:06
【问题描述】:

我正在研究 Rule of Zero 并且有 2 个问题,用于演示规则的最后一段代码。

class module {
    public:
        explicit module(std::wstring const& name)
        : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary } {}

        // other module related functions go here

    private:
        using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;

        module_handle handle;
    };
  1. 为什么使用大括号而不是括号来初始化句柄?
  2. 使用 module_handle = std::unique_ptr 有什么作用;在这种情况下到底是什么意思?是否可以用 typedef 替换它?

【问题讨论】:

    标签: c++ c++11 idioms rule-of-zero


    【解决方案1】:

    为什么使用大括号而不是括号来初始化句柄?

    是C++11中引入的uniform initializing方法

    使用 module_handle = std::unique_ptr 有什么作用;在这种情况下到底是什么意思?是否可以用 typedef 替换它?

    它是template aliasing,也用于创建新类型。 usingtypedef 的新的强大替代品

    【讨论】:

    • @M.M "using is a new replacement of typedef" 也是从 C++11 开始?
    • @goldcode:是的——我强烈推荐你阅读this article
    【解决方案2】:
    explicit module(std::wstring const& name)
        : handle { ::LoadLibrary(name.c_str()), &::FreeLibrary }
    

    1) 这是 C++11 的新(统一)初始化列表语法。它将 2 个参数传递给构造函数,该函数将初始化变量 handle。在这种情况下,它实际上与

    explicit module(std::wstring const& name)
        : handle(::LoadLibrary(name.c_str()), &::FreeLibrary)
    

    它将调用std::unique_ptr 的构造函数,该构造函数接受一个“指针”(在本例中为句柄)和一个删除器(FreeLibrary 的地址)。

    using module_handle = std::unique_ptr<void, decltype(&::FreeLibrary)>;
    

    2) 这与 typedef 基本相同。如需更多信息,请参阅this

    【讨论】:

      【解决方案3】:
      1. 为什么使用大括号而不是括号来初始化句柄?

      其他人已经说过什么花括号是(统一初始化),但没有人解释为什么在括号应该也可以工作的情况下在这里使用它。

      这个想法是,与其学习所有不同的初始化语法,C++11 使人们能够简单地学习一种语法并在任何地方使用它;统一初始化是添加的功能之一,目的是让学习编写 C++ 变得更容易(以及其他原因)。

      需要注意的一点是,统一初始化是统一的,因为它适用于所有初始化上下文并且可以进行不同类型的初始化(例如,聚合初始化与使用构造函数),但这并不意味着任何初始化可以使用它来完成;具体来说,有一些极端情况可以定义类型,以便统一初始化无法访问某些构造函数。答案是根本不应该编写这样的构造函数。不幸的是,有些已经内置到标准库中,vector&lt;int&gt;(4, 5)vector&lt;int&gt;{4, 5} 是常见的例子。

      2。什么 using module_handle = std::unique_ptr;在这种情况下到底是什么意思?是否可以用 typedef 替换它?

      这只是 typedef 的不同语法。该语法比 typedef 更强大,因为它可以被模板化,但是在这种情况下没有使用它。这个例子可以使用 typedef 来实现。

      更喜欢新语法的原因是因为它是一种更以类型为中心的、类似 C++ 的语法。

      旧的 typedef 语法使用 C 的“声明模仿使用”规则。也就是说,使用指针看起来像*ptr,因此声明指针使用相同的表达式int *ptr;。使用返回指向函数的指针的函数看起来像 (*foo())(),因此再次声明使用相同的表达式 int (*foo())();。这种语法是以表达式为中心的,你写出一个表达式,然后语言推导出变量的隐含类型。

      这种语法的问题在于它让很多人感到困惑,并且随着时间的推移,C 和 C++ 都发生了与这个最初理想不一致的各种变化。例如,在 C 语言中声明一个函数最初就严格遵循此规则;在 C 的早期版本中,您可以使用 int foo(x, y) 声明一个函数,它完全模仿了函数调用表达式 foo(x, y)。后来很明显,类型安全和检查很重要,并且在 ISO C 中声明一个函数看起来像 int foo(int x, int y),它并没有如此紧密地模仿函数的用法。

      C++ 更进一步,例如引入引用。由于引用用法看起来与使用常规对象没有任何不同,因此没有可以添加到声明中以遵循“声明模仿使用”的语法。相反,他们只是决定不遵守规则,而只是选择不会与之冲突的语法。

      C++ 也比 C 更加强调类型。通过类型、基于类型的重载等参数化的模板。

      因此,既因为旧语法似乎天生就有问题,又因为 C++ 更重视类型而不是表达式,所以 C++11 为类型别名引入了这种新语法。它不是晦涩的语法,而是简单的using &lt;type alias&gt; = &lt;type&gt;;。不需要“螺旋规则”或“从右到左”规则。

      这种语法不仅可以完全替代 typedef,它还增加了直接模板化的能力,替代了模板类 hack 中长期以来需要的旧 typedef。同样,新语法是一项附加功能,可以更轻松地学习编写 C++。

      【讨论】:

      • 大括号初始值设定项不会选择显式构造函数,并且可用的类型转换也会更少(并且您确实希望大多数构造函数都是显式的)。我不会扔掉好的 ol' 初始化器。它们与新的一起使用。
      • @AlexandreC。您可以使用Type{x} 而不是{x} 和不使用复制初始化(而不是复制初始化S s = {x} 使用直接初始化S s{x})来获得显式初始化器。我还认为避免大多数转化是一件好事。
      猜你喜欢
      • 2018-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      相关资源
      最近更新 更多