Что-то вроде "if constexpr", но для определения класса
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 не работает для не class-scope:
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
работает только в функциональных телах-но мои вопросы:
1 квартале как достичь подобного эффекта, как if-constexpr
в функциях-для класса и глобальной области в C++1z / c++14? И я не прошу здесь еще одного объяснения специализации шаблонов... Но что-то, что имеет подобную простоту, как if constexpr
...
Q2 там любой план расширения C++ для вышеупомянутых областей?
3 ответов
как достичь аналогичного эффекта, такого как 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
принимает набор модулей времени компиляции и говорит, что индекс первого во время компиляции. Если вы передадите его N модулей времени компиляции, он вернет N, если все false:
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
принимает набор модулей времени компиляции и возвращает лямбду. Эта лямбда возвращает через perfect forwarding первый элемент, который соответствует первому истинному времени компиляции 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"
);
который должен приближаться к коммутатору времени компиляции. Мы могли бы более тесно связать булы с аргументами с немного большей работой.
код не скомпилирован, вероятно, содержит 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 будет / будет хорошей причиной снова поднять эту тему...