我喜欢跨度,所以我在这里和那里使用gsl::span
。但是 - 在 C++20 中,它将改为std::span
*。我使用std::optional
,但对于 C++14 代码,它需要std::experimental::optional
.等等。
在这些变体(有时可能超过两个(之间进行编译时选择的惯用和面向未来的方法是什么,以便我的实际代码只能使用一个编译成正确选择的跨度的标记序列,或可选,或其他类似的结构?
注意:我想避免污染全局命名空间。
* 嗯,从技术上讲,我以后也可以使用gsl::span
,但这个问题的想法是一旦标准可用就使用标准中的内容,在此之前使用最近的替代方案。
我通常使用这样的东西:
#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;
这个问题是错误的,因为即使有这样的"令牌序列",也不能保证这两种选择的行为相同。
考虑experimental::optional
与std::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
当然,这里的问题是你必须包含<version>
,它本身就是一个 C++20 标头。因此,此代码仅适用于至少部分符合 C++20 的编译器。
一种方法是在迁移命名空间中使用包含合适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<T>
用于您的代码,该代码使用optional
-du-jour 愉快地与其他代码交互。一旦兼容性问题消失,您可以在适当的时候替换您的自定义资格。但是请注意,这些定义之间存在一些差异,即您需要坚持通用功能。
对 @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