代码之家  ›  专栏  ›  技术社区  ›  Lingxi

C中的默认参数vs重载++

  •  16
  • Lingxi  · 技术社区  · 7 年前

    例如,而不是

    void shared_ptr::reset() noexcept;
    template <typename Y>
    void shared_ptr::reset(Y* ptr);
    

    人们可能会想到

    template <typename Y = T>
    void shared_ptr::reset(Y* ptr = nullptr);
    

    我认为这里的性能差异可以忽略不计,第二个版本更简洁。C++标准走第一条路有什么具体原因吗?

    这个 same question 已要求提供Kotlin语言,默认参数是首选参数。

    更新:

    std::unique_ptr::reset() 遵循默认参数设计(请参见 here ). 所以我认为 std::shared_ptr::reset() 使用重载是因为它们具有不同的异常规范。

    4 回复  |  直到 7 年前
        1
  •  29
  •   StoryTeller - Unslander Monica    7 年前

    关键的区别在于,这两个操作实际上在语义上并不相同。

    第一个意思是离开 shared_ptr 没有托管对象。第二个是让指针管理 另一个 对象这是一个重要的区别。在一个函数中实现它意味着我们将让一个函数执行两个不同的操作。

    此外,每个操作可能对所讨论的类型有不同的约束。如果我们将它们转储到一个函数中,“两个分支”将必须满足相同的约束,这是不必要的限制。C++17和 constexpr if 缓解它,但这些功能是在退出之前指定的。

    最终,我认为这个设计符合ScottMeyers的建议。如果默认参数让您执行语义不同的操作,则可能是另一个重载。


    好的,来解决你的编辑问题。是的,例外规范不同。但正如我之前提到的 原因 它们可以不同,就是功能在做不同的事情。这个 semantics of the reset members 要求:

    void reset() noexcept;
    

    影响 :相当于 shared_­ptr().swap(*this) .

    template<class Y> void reset(Y* p);
    

    影响 :相当于 shared_­ptr(p).swap(*this) .

    那里没有什么新闻快讯。每个函数都具有构造新 共享\u ptr 使用给定的参数(或缺少),并交换。那么 共享\u ptr 施工人员怎么做?根据 a preceding section ,他们会这样做:

    constexpr shared_ptr() noexcept;
    

    影响 :构造一个空的shared\uu­ptr对象。
    后置条件 : use_­count() == 0 && get() == nullptr .

    template<class Y> explicit shared_ptr(Y* p);
    

    后置条件 : use_­count() == 1 && get() == p . 投掷 : bad_­alloc ,或在无法获取内存以外的资源时出现实现定义的异常

    请注意指针使用计数上的不同post条件。这意味着第二个过载需要考虑任何内部簿记。并很可能为其分配存储。这两个重载构造函数做不同的事情,正如我前面所说的,这是将它们划分为不同函数的强烈提示。事实上,我们可以得到更有力的例外保证,这进一步证明了设计选择的合理性。

    最后,为什么 unique_ptr 两个操作只有一个重载?因为默认值不会更改语义。它只需要跟踪新的指针值。值为null的事实(无论是来自默认参数还是其他参数)不会彻底改变函数的行为。因此,单一过载是合理的。

        2
  •  7
  •   Mats Petersson    7 年前

    如果您经常重置为 nullptr 而不是一个新值,然后是单独的函数 void shared_ptr::reset() noexcept; 将具有空间优势,因为您可以对所有类型使用一个函数 Y ,而不是使用 Y 每种类型的类型 Y . 另一个空间优势是,没有参数的实现不需要将参数传递到函数中。

    当然,如果函数被多次调用,这两者都不重要。

    异常行为也存在差异,这可能非常重要,我认为这就是为什么标准有多个此函数声明的动机。

        3
  •  6
  •   anderas    7 年前

    虽然其他答案的设计选择都是有效的,但它们确实假设了一件在这里并不完全适用的事情:语义等价!

    void shared_ptr::reset() noexcept;
                          // ^^^^^^^^
    template <typename Y>
    void shared_ptr::reset(Y* ptr);
    

    第一个过载是 noexcept ,而第二个重载不是。无法确定 无例外 -ness基于 运行时值 ,因此需要不同的重载。

    关于不同原因的一些背景信息 无例外 规格: reset() 不会引发,因为假定先前包含的对象的析构函数不会引发。但是第二个重载可能还需要为共享指针状态分配一个新的控制块,这将引发 std::bad_alloc 如果分配失败。(和 reset ting到a nullptr 无需分配控制块即可完成。)

        4
  •  3
  •   Christophe    7 年前

    重载和默认指针之间有一个根本区别:

    • 重载是自包含的:库中的代码完全独立于调用上下文。
    • 默认参数不是自包含的,但取决于调用上下文中使用的声明。它可以在给定的范围内通过简单的声明重新定义(例如,不同的默认值,或者不再有默认值)。

    因此,从语义上讲,默认值是嵌入在调用代码中的捷径,而重载是嵌入在被调用代码中的含义。

    推荐文章