Что-то вроде "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 будет / будет хорошей причиной снова поднять эту тему...