【问题标题】:Inject namespace experimental to std将命名空间实验注入 std
【发布时间】:2016-04-10 07:32:58
【问题描述】:

像下面这样将命名空间std::experimental 注入std 是好是坏?

namespace std
{
namespace experimental
{
}
using namespace experimental;    
}

#include <experimental/optional>

int main()
{
    std::optional< int > o;
    return 0;
}

或者更现代的形式:

#if __has_include(<optional>)
# include <optional>
#elif __has_include(<experimental/optional>)
# include <experimental/optional>
namespace std
{
using namespace experimental;    
}
#else
#error !
#endif

int main()
{
    std::optional< int > o;
    return 0;
}

引入std::experimental“子命名空间”的意图很明确,因为std::experimental 当前包含a plenty of new libraries。我认为很可能所有这些都将迁移到namespace std 而无需任何实质性更改,并且当前编写的用户代码可以依赖于此(我完全错了吗?)。否则,所有这些代码都应该被重构以在将来从std::experimental:: 更改为std::。这没什么大不了的,但可能有理由不这样做。

问题是关于生产代码和不太严重的代码。

【问题讨论】:

  • 我们是在谈论可移植的生产代码吗?
  • @MikeMB 说,我们谈论的是开源大型广泛使用的项目(与人类生命安全无关)。
  • [namespace.std]:“除非另有说明,否则如果 C++ 程序将声明或定义添加到命名空间 std 或命名空间 std 内的命名空间,则其行为未定义。”跨度>
  • @user657267 因此应该将问题简化为“它是明确指定的还是隐含指定的?”?

标签: c++ c++11 stl c++14


【解决方案1】:

在工作中,我花了很多时间与这种推测的复杂性作斗争(我正在寻找一个描述行为特征的好词组,这是迄今为止我想出的最好的词组)。

在我看来,您现在在代码中引入了复杂性和风险,以避免将来进行重构。这已经够糟糕了,但在这种特殊情况下,我什至会说重构是一个糟糕的词选择——与其说重构不如简单的文本替换。

检查代码以删除 std::experimental(或更改为 std:: 或 std::something_else )实际上只是文本替换。在最好的情况下,您正在编辑器或 ide 中查看快速命令。在最坏的情况下,您正在考虑花费几个小时编写正则表达式——可能在您的编辑器中,可能在 PERL 或 Ruby 中......

那些 std::experiment 表明您正在使用实验性功能,最好在代码中明确说明这一点。如果并且当某些库进入标准时,称职的程序员可以快速轻松地进行必要的文本替换。如果您的文本编辑技能不能胜任这项任务,请不要破坏语言——提高您的文本编辑技能。这是一个机会。

更一般地说,编写最简单、最干净的代码,现在满足您的需求,并准备好在情况发生变化时立即更改它。这通常会产生更灵活的代码,比您现在的猜测更好地支持您未来的实际需求。

【讨论】:

    【解决方案2】:

    听起来是个坏主意。

    首先,这是未定义的行为。标准草案 N4140 说:

    [namespace.std]/1: 除非另有说明,否则如果 C++ 程序将声明或定义添加到命名空间 std 或命名空间 std 内的命名空间,则其行为未定义。 [...]

    using-directive 是一种声明,因此 UB 是当天的顺序。

    其次,std::experimental 中的内容非常容易发生变化。您可能会发现,当将内容移至 std 时,您的代码仍然可以编译,但不会以完全相同的方式运行。这只是自找麻烦,尤其是在生产代码中。

    【讨论】:

    • 您甚至不必走那么远。 using-directive 是一个声明。
    【解决方案3】:

    std::experimental 中的许多库可以并且将会在迁移到 std 之前以破坏用户代码的方式进行更改,甚至可能不会迁移到 std。这就是他们将它们放在std::experimental 的原因。

    std::experimental 旨在提供一个相对免费的地方,您可以在其中为 C++ 引入新的提议库功能,编译器可以在不破坏现有代码的情况下实现它们。实现是不断变化的,不是标准的。其中一些可能会进入 C++1z,但其中一些可能不会,那些进入的可能会被更改,而那些没有进入的可能会通过实质性的变化进入 C++2x .

    只需看看 Ranges 的历史。或者微软提出的协程/可恢复功能。或反思工作组。或概念。

    我敢说,这次 C++ 迭代的目标是敏捷并快速失败。许多独立提案通过管道进行,目标是最大限度地减少相互依赖。如果一个给定的提案在 C++ 标准的黄金时间还没有准备好,它就不会进入。如果它已经准备好并且值得,它会被模块化添加。

    (据我了解)这是为了避免出现混乱,即流程的某些部分“太大而不能失败”,以及尚未准备好发布到标准中的事情,或者标准发布被推迟多年,因为有些东西还没准备好。

    除此之外,像其他答案所指出的那样,以这种方式处理 std 会使您的程序格式错误,不需要诊断。

    【讨论】:

      【解决方案4】:

      您正在做的事情引入了未定义的行为。一般来说,我会说引入未定义的行为更多地属于不良实践领域,而不是良好实践领域。

      根据 1998 C++ 标准,第 17.4.3.1 节,第 1 段。

      C++ 程序添加声明或定义是未定义的 命名空间 std 或命名空间 std 内的命名空间,除非另有说明 指定的。程序可以为任何标准添加模板特化 库模板到命名空间 std。这样的专业化(完整或 标准库模板的部分)导致未定义的行为 除非声明依赖于用户定义的外部名称 链接,除非专业化符合标准库 对原始模板的要求。

      我没有该标准的更新版本(在我当前的机器上),但根据记忆,C++ 标准的所有版本都有类似的条款。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-03
        • 2016-04-28
        • 2020-09-10
        • 1970-01-01
        • 1970-01-01
        • 2010-12-25
        • 1970-01-01
        • 2013-09-03
        相关资源
        最近更新 更多