Какой идиоматический способ ссылаться на std::what или not_yet_in_std::what?

Мне нравятся пролеты, поэтому я использую gsl::span здесь и там. Но - в C++20 это будет std::span вместо *. я использую std::optional, но для кода C++14 это должно быть std::experimental::optional, И так далее.

Какой идиоматичный и своего рода перспективный способ сделать выбор во время компиляции между этими вариантами (иногда, возможно, более двух), так что мой реальный код может просто использовать одну последовательность токенов, которая компилируется в правильно выбранный span или опционально, или другая подобная конструкция?

Примечание. Я хочу избежать загрязнения глобального пространства имен.


* Ну, технически я мог бы использовать gsl::span позже, но идея в этом вопросе состоит в том, чтобы использовать то, что в стандарте, как только это будет доступно, и ближайшую альтернативу, до этого.

4 ответа

Я обычно использую что-то вроде этого:

#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
Другие вопросы по тегам