问题
I would like to use constexpr if
to branch at compile time, but it does not seem to be supported by the latest MSVC compiler. Is there an alternative to the following?:
template<typename T>
void MyFunc()
{
if constexpr(MeetsConditions<T>::value)
{
FunctionA<T>();
}
else
{
FunctionB<T>();
}
}
In short: Can I simulate constexpr if
when it is not supported by the compiler?
回答1:
One of pre-C++17 ways is to use partial template specializations, like here:
template <template T, bool AorB>
struct dummy;
template <typename T, true>
struct dummy {
static void MyFunc() { FunctionA<T>(); }
}
template <typename T, false>
struct dummy {
static void MyFunc() { FunctionB<T>(); }
}
template <typename T>
void Facade() {
dummy<T, MeetsConditions<T>::value>::MyFunc();
}
If you need more, than 2 specializations - you can use enum or integral value, and make specializations for all needed enums.
Another way is to use std::enable_if:
template <typename T>
std::enable_if<MeetsConditions<T>::value, void>::type
MyFunc() {
FunctionA<T>();
}
template <typename T>
std::enable_if<!MeetsConditions<T>::value, void>::type
MyFunc() {
FunctionB<T>();
}
回答2:
You can do it the old fashioned, tried and tested tag dispatch way:
template<typename T>
void MyFuncImpl(std::true_type) {
FunctionA<T>();
}
template<typename T>
void MyFuncImpl(std::false_type) {
FunctionB<T>();
}
template<typename T>
void MyFunc()
{
MyFuncImpl<T>(std::integral_constant<bool, MeetsConditions<T>::value>{});
}
回答3:
There are several alternatives indeed (which have been in use long before if constexpr
started to exist).
One is tag dispatch:
template <class T>
void Function(std::true_type)
{
FunctionA<T>();
}
template <class T>
void Function(std::false_type)
{
FunctionB<T>();
}
template <class T>
void MyFunc()
{
Function<T>(std::integral_constant<bool, MeetsCondition<T>::value>{});
}
Another one are traits:
template <bool B>
struct FunctionTraits;
template <>
struct FunctionTraits<true>
{
template <class T>
static void Call() { FunctionA<T>(); }
};
template <>
struct FunctionTraits<false>
{
template <class T>
static void Call() { FunctionB<T>(); }
};
template <class T>
void MyFunc()
{
FunctionTraits<MeetsCondition<T>::value>::Call<T>();
}
回答4:
If you are using C++ 14 and Boost, consider using Hana. Implemented using Hana, this looks something like this:
template<typename T>
void MyFunc()
{
hana::eval_if(MeetsConditions<T>::value,
[](auto) { FunctionA<T>(); },
[](auto _) { FunctionB<T>(_(exprThatWouldOtherwiseBeAnError)); }
);
}
For the specific case of detecting SFINAE and executing something only in that case, that could be as simple as:
template<typename T>
void MyFunc()
{
auto maybeDoFunctionA = hana::sfinae([]() -> decltype((void) FunctionA<T>()) {
FunctionA<T>();
});
}
回答5:
if constexpr
is a C++17 feature; before C++17, starting from C++11, you can use SFINAE with std::enable_if
template<typename T>
typename std::enable_if<true == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionA<T>(); }
template<typename T>
typename std::enable_if<false == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionB<T>(); }
-- EDIT --
If you can use only a C++98 compiler, implement a type traits that work like std::enable_if
is really simple; see the following example
template <bool, typename = void>
struct enableIf
{ };
template <typename T>
struct enableIf<true, T>
{ typedef T type; };
and the functions become
template<typename T>
typename enableIf<true == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionA<T>(); }
template<typename T>
typename enableIf<false == MeetsConditions<T>::value>::type MyFunc ()
{ FunctionB<T>(); }
来源:https://stackoverflow.com/questions/43587405/constexpr-if-alternative