if constexpr
это большой шаг для избавления от препроцессора в программах на C ++. Однако это работает только в функциях — как в этом примере:
enum class OS
{
Linux,
MacOs,
MsWindows,
Unknown
};
#if defined(__APPLE__)
constexpr OS os = OS::MacOs;
#elif defined(__MINGW32__)
constexpr OS os = OS::MsWindows;
#elif defined(__linux__)
constexpr OS os = OS::Linux;
#else
constexpr OS os = OS::Unknown;
#endif
void printSystem()
{
if constexpr (os == OS::Linux)
{
std::cout << "Linux";
}
else if constexpr (os == OS::MacOs)
{
std::cout << "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
std::cout << "MS Windows";
}
else
{
std::cout << "Unknown-OS";
}
}
Но мечты об избавлении от препроцессора не совсем сбываются, потому что следующие примеры не компилируются:
1 Нельзя использовать его в определении класса для определения некоторых членов класса по-разному:
class OsProperties
{
public:
static void printName()
{
std::cout << osName;
}
private:
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
};
2 И это не работает для не класса:
if constexpr (os == OS::Linux)
{
const char* const osName = "Linux";
}
else if constexpr (os == OS::MacOs)
{
const char* const osName = "MacOS";
}
else if constexpr (os == OS::MsWindows)
{
const char* const osName = "MS Windows";
}
else
{
const char* const osName = "Unknown";
}
Я (почти) уверен, что это согласно спецификации C ++ 17, что if constexpr
работает только внутри функциональных органов — но мои вопросы:
Q1 Как добиться подобного эффекта вроде if-constexpr
в функциях — для класса и глобальной области видимости в C ++ 1z / C ++ 14? И я не прошу здесь еще одного объяснения специализации шаблона … Но то, что имеет такую же простоту, как if constexpr
…
Q2 Есть ли план по расширению C ++ для вышеупомянутых областей?
Как добиться такого же эффекта, как if-constexpr в функциях — для класса и глобальной области видимости в C ++ 1z / C ++ 14? И я не прошу здесь еще одного объяснения специализации шаблона …
Вы в основном просто сказали: «Я хочу специализацию шаблона, но без всего этого надоедливого специализация шаблона.»
if constexpr
инструмент для изменения поведения функций на основе конструкций времени компиляции Шаблонная специализация — это инструмент, который C ++ предоставляет для создания определения изменение на основе конструкций времени компиляции. Это единственный инструмент, который C ++ обеспечивает для этой функциональности.
Теперь для упрощенного случая инициализации переменной вы всегда можете создать и вызвать лямбду. C ++ 17 предложений constexpr
поддержка лямбд, и лямбда сможет использовать if constexpr
решить, какое значение вернуть.
Есть ли план по расширению C ++ для вышеупомянутых областей?
Нет. Вот все предложения, и никто из тех, кто за последние пару лет не углубился в эту область.
И вряд ли они когда-либо будут.
Тип индекса:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth
берет набор bool во время компиляции и говорит, что индекс первого находится во время компиляции. Если вы передадите ему N bools во время компиляции, он вернет N, если все ложные:
constexpr index<0> first_truth() { return {}; }
template<class...Rest>
constexpr index<0> first_truth(std::true_type, Rest...) { return {}; }
template<class...Rest>
constexpr auto first_truth(std::false_type, Rest...rest) {
return index<first_truth( rest... )+1>{};
}
dispatch
принимает набор bool-файлов времени компиляции и возвращает лямбду Эта лямбда-функция возвращается через совершенную пересылку первого элемента, который соответствует первому истинному времени компиляции bool:
template<class...Bools>
constexpr auto dispatch(Bools...bools) {
constexpr auto index = first_truth(bools...);
return [](auto&&...fs){
return std::get< decltype(index){} >(
std::forward_as_tuple( decltype(fs)(fs)... )
);
};
}
Тип bool времени компиляции:
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<bool b>
bool_t<b> bool_k{};
Теперь мы решаем вашу проблему:
const char* const osName =
dispatch(
bool_k<os == OS::Linux>,
bool_k<os == OS::MacOs>,
bool_k<os == OS::MsWindows>
)(
"Linux",
"MacOS",
"MS Windows",
"Unknown");
который должен приближаться к переключателю времени компиляции. Мы могли бы связать bools более близко к аргументам с немного большим количеством работы.
Код не скомпилирован, возможно, содержит tpyos.
Как определить различные типы на основе некоторой постоянной времени компиляции без специализации шаблона?
Вот:
constexpr auto osPropsCreate()
{
if constexpr (os == OS::Linux) {
struct Props { const char* name; int props1; using handle = int; };
return Props{"linux", 3};
} else if constexpr (os == OS::MacOs) {
struct Props { const char* name; using handle = float; };
return Props{"mac"};
} else if constexpr (os == OS::MsWindows) {
struct Props { const char* name; using handle = int; };
return Props{"win"};
} else
return;
}
using OsProps = decltype(osPropsCreate());
constexpr OsProps osProps = osPropsCreate();
Как видите — я использовал новую конструкцию if constexpr
создать из некоторой функции «реализации» тип, который зависит от постоянной времени компиляции. Это не так просто в использовании, как static if
на языке D — но это работает — я могу сделать это:
int linuxSpecific[osProps.props1];
int main() {
std::cout << osProps.name << std::endl;
OsProps::handle systemSpecificHandle;
}
Следующая вещь — определить различные функции в зависимости от постоянной времени компиляции:
constexpr auto osGetNameCreate() {
if constexpr (os == OS::Linux) {
struct Definition {
static constexpr auto getName() {
return "linux";
}
};
return Definition::getName;
} else if constexpr (os == OS::MacOs) {
// we might use lambda as well
return [] { return "mac"; };
} else if constexpr (os == OS::MsWindows) {
struct Definition {
static constexpr auto getName() {
return "win";
}
};
} else
return;
}constexpr auto osGetName = osGetNameCreate();
int main() {
std::cout << osGetName() << std::endl;
}
На самом деле они могут быть объектоподобными объектами (функторами) или статическими функциями-членами из вложенных классов. Это не имеет значения — у каждого есть полная свобода определять разные вещи для разных констант времени компиляции (тип ОС в данном случае). Обратите внимание, что для неизвестной системы мы просто возвращаем void
— это приведет к ошибке компиляции для неизвестной системы …
Отвечая на второй вопрос:
Первый ответ приведите в комментариях (ссылка на сайт). Моя интерпретация заключается в том, что стандартный комитет C ++ не готов к этому изменению. Возможно, соревнование с D будет / будет хорошей причиной, чтобы поднять эту тему еще раз …