if constexpr
is a big step for getting rid of preprocessor in C++ programs. However it works only in functions - like in this example:
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";
}
}
But dreams about getting rid of preprocessor are not quite satisfied - because the following examples do not compile:
1 Cannot use it in class definition to define some members of class differently:
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 Nor it works for not class-scope (like global 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";
}
I am (almost) sure this is per C++17 specification that if constexpr
works only within function bodies - but my questions are:
Q1 How to achieve the similar effect like if-constexpr
in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization... But something that has similar simplicity as if constexpr
...
Q2 Are there any plan to extend C++ for the above mentioned scopes?
Short answer: static_assert(false) should never appear in a constexpr if expression, regardless of whether it's in a template function or whether it's in the discarded branch.
Constexpr if If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.
How to achieve the similar effect like if-constexpr in functions - for class and global scope in C++1z/C++14? And I am not asking here for yet another explanation of template specialization...
You basically just said, "I want template specialization, but without all that pesky template specialization."
if constexpr
is the tool for making the behavior of functions change based on compile-time constructs. Template specialization is the tool that C++ provides for making definitions change based on compile-time constructs. It is the only tool C++ provides for this functionality.
Now for your simplistic case of initializing a variable, you can always create and call a lambda. C++17 offers constexpr
support for lambdas, and a lambda would be able to use if constexpr
to decide what value to return.
Are there any plan to extend C++ for the above mentioned scopes?
No. Here are all of the proposals, and none of the ones from the past couple of years delve into this domain.
And it's highly unlikely they ever will.
An index type:
template<std::size_t I>
using index = std::integral_constant<std::size_t, I>;
first_truth
takes a set of compile-time bools and says what the index of the first one is at compile time. If you pass it N compile-time bools, it returns N if all are 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
takes a set of compile-time bools and returns a lambda. This lambda returns via perfect forwarding the first element that matches the first true compile time 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)... )
);
};
}
A compile time bool type:
template<bool b>
using bool_t = std::integral_constant<bool, b>;
template<bool b>
bool_t<b> bool_k{};
Now we solve your problem:
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"
);
which should approximate a compile-time switch. We could tie the bools more closely to the arguments with a bit more work.
Code not compiled, probably contains tpyos.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With