Мне нравятся пролеты, поэтому я использую gsl::span
здесь и там. Но — в C ++ 20 это будет std::span
вместо*. я использую std::optional
, но для кода C ++ 14 это должно быть std::experimental::optional
, И так далее.
Какой идиоматичный и своего рода перспективный способ сделать выбор во время компиляции между этими вариантами (иногда, возможно, более двух), так что мой реальный код может просто использовать одну последовательность токенов, которая компилируется в правильно выбранный span или опционально, или другая подобная конструкция?
Примечание. Я хочу избежать загрязнения глобального пространства имен.
* Ну, технически я мог бы использовать 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-Жур. Как только проблема совместимости исчезнет, вы сможете заменить свою специальную квалификацию в любое время. Однако обратите внимание, что между этими определениями есть некоторые различия, т. Е. Вам нужно придерживаться общей функциональности.
Адаптация подхода @ 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