【问题标题】:What's an idiomatic way to refer to either std::whatever or not_yet_in_std::whatever? [closed]引用 std::whatever 或 not_yet_in_std::whatever 的惯用方式是什么? [关闭]
【发布时间】:2019-03-10 05:41:43
【问题描述】:

我喜欢跨度,所以我在这里和那里使用gsl::span。但是 - 在 C++20 中,它将改为 std::span*。我使用std::optional,但是对于C++14 代码,它需要是std::experimental::optional。以此类推。

什么是在这些变体(有时可能超过两个)之间进行编译时选择的惯用且面向未来的方法,以便我的实际代码可以只使用一个标记序列编译成正确选择的跨度,或可选的,或其他类似的构造?

注意:我想避免污染全局命名空间。


* 好吧,从技术上讲,我以后也可以使用gsl::span,但这个问题的想法是在标准可用时使用标准中的内容,并在此之前使用最接近的替代方案。

【问题讨论】:

  • "在 C++20 中,它需要改为 std::span" 为什么它需要?您的代码不会仅仅因为您可以使用另一种 span 类型而停止工作。至于experimental::optional,这甚至不是要求在C++14实现中实现的。
  • 为什么不能使用宏? ABI 兼容性?如果您需要 ABI 兼容性,您可能会感到厌烦,但谁知道呢,有人可能会让我大吃一惊……
  • @NicolBolas:见编辑。
  • @Justin:但是包含宏仍然不能解决整个问题。你会写什么而不是int foo(std::span sp)? ...并建议您将答案写为问题的答案。

标签: c++ idioms stdoptional std-span


【解决方案1】:

我通常使用这样的东西:

#if some_kind_of_test_here_not_necessarily_a_macro
namespace stdx = std;
#elif some_other_test_here
namespace stdx = std::experimental;
#else
#error "Some Message"
#endif

现在在您的代码中使用:

stdx::span  mySpan;

【讨论】:

  • 嗯,stdx。有趣的。这有多普遍?您是否在任何广泛使用的 FOSS 库中这样做过?
  • @Justin: 吹毛求疵...见 dit.
  • 我希望您选择的stdx 不会与msvc 冲突。
【解决方案2】:

这个问题是错误的,因为即使有这样一个“令牌序列”,也不能保证两个备选方案表现相同。

考虑experimental::optionalstd::optional。后者,在 C++17 的缺陷报告之后,如果T 可轻松复制,则要求可轻松复制。 experimental::optional 不是。如果你依赖它来构建 C++17,你不知道它是否适用于 C++14。

gsl::span 问题不大,因为 GSL 实现可能会跟踪 std::span 的更改,因为它已被合并到 C++20 中。

但是,如果您坚持这一点,C++20 将使功能测试宏成为强制性的。所以你可以使用这样的宏技术:

#include <version>
#ifdef <insert span test macro here>
#include <span>
template<typename T, std::ptrdiff_t N>
using span = std::span<T, N>;
#else
#include <gsl/span>
template<typename T, std::ptrdiff_t N>
using span = gsl::span<T, N>;
#endif

当然,这里的问题是你必须包含&lt;version&gt;,它本身就是一个C++20的头文件。因此,此代码仅适用于至少部分符合 C++20 的编译器。

【讨论】:

  • 感谢您的第一点。我不认为它可以避免这个问题,因为你可以小心并检查微不足道的复制能力,或者只是使用它,这样它就没什么大不了了。关于您的 C++20 示例 - 这会污染全局命名空间,如果我正在编写库,我不想这样做。
  • @einpoklum:然后随意将using 声明放在命名空间的某个地方。基本思想保持不变:包含适当的文件并为该类型创建别名。
【解决方案3】:

一种方法是在迁移命名空间中使用包含合适的using 别名的迁移标头,例如:

#if __cplusplus < 201402L
#include <experimental/optional>
namespace mig {
    template <typename T>
    using optional = std::experimental::optional<T>;
}
#else
#include <optional>
namespace mig {
    template <typename T>
    using optional = std::optional<T>;
}
#endif

在迁移时,您将包含相应的标头并使用 mig::optional&lt;T&gt; 作为您的代码,该代码使用 optional-du-jour 与其他代码愉快地交互。一旦兼容性问题消失,您可以在合适的时候替换您的自定义资格。但是请注意,这些定义之间存在一些差异,即,您需要坚持使用通用功能。

【讨论】:

  • 但是谁使用mig?至少@MartinYork 的stdx 建议是直观的(无论如何对我来说)-“它就像标准但不完全标准”。 mig 可能会让你想起俄罗斯飞机...
  • @einpoklum:您显然可以使用您认为合适的任何命名空间名称。假设多人使用类似的方法,建议使用某种形式的唯一名称以避免别名冲突。
  • 我实际上在想,也许 common 名称可能会更好 - 对于冲突,要么使用一些模块,要么使用带有包含保护的单个通用 stdx-namespace-filling 标头.
【解决方案4】:

改编自 @MartinYork 的方法,(希望)适用于单一构造级别而不是整个命名空间级别:

#if __cplusplus >= 202001L
#include <span>
namespace stdx {
template <class ElementType, std::ptrdiff_t Extent = std::dynamic_extent>
using span = std::span<ElementType, Extent>;
} // namespace stdx
#else
#include <gsl/span>
namespace stdx {
template <class ElementType, std::ptrdiff_t Extent = gsl::dynamic_extent>
using span = std::span<ElementType, Extent>;
} // namespace stdx
#endif // __cplusplus >= 202001L

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 2012-01-11
    • 1970-01-01
    • 2016-05-30
    • 2012-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多