Is there a standard way for me to select a type at compile-time in c 20 when the type depends on compile-time information available later in the function, i.e. the type is "deferred" because of intermediate compile-time dependencies.
For example something like this which depends on the auto
keyword but does not compile:
template<bool value, typename ...>
struct bool_type : std::integral_constant<bool , value> {};
template<typename T>
void function(T* v) {
auto r;
bool different_type = false;
if constexpr (...)
r = (T)subfunc_a(v);
else if constexpr (...)
r = (T)subfunc_b(v);
else if constexpr (...)
r = (T)subfunc_c(v);
else if constexpr (...);
r = (T)subfunc_d(v);
else if constexpr (...)
r = (T)subfunc_e(v);
// This condition depends on previous conditions. Extracting the condition
// to the top of this function for use with `std::conditional` would be
// burdensome. Decoupling the conditional in this way also risks errors. I
// want to depend on the type system to enforce code correctness.
else if constexpr (...) {
r = (long)subfunc_f(v);
different_type = true;
}
else if constexpr (...) {
r = (unsigned long)subfunc_g(v);
different_type = true;
}
else {
static_assert(bool_type<false, T>::value, "Unsupported type");
}
do_common_work();
if (different_type)
do_more_work();
*v = r;
}
Or this example which depends on the static if
proposal which prevents if constexpr
conditionals from creating a new scope. The proposal didn't pass so the code doesn't compile.
template<typename T>
void function(T* v) {
bool different_type = false;
if constexpr (...)
T r = subfunc_a(v);
else if constexpr (...)
T r = subfunc_b(v);
else if constexpr (...)
T r = subfunc_c(v);
else if constexpr (...);
T r = subfunc_d(v);
else if constexpr (...)
T r = subfunc_e(v);
else if constexpr (...) {
different_type = true;
long r = subfunc_f(v);
}
else if constexpr (...) {
different_type = true;
unsigned long r = subfunc_g(v);
}
else {
static_assert(bool_type<false, T>::value, "Unsupported type");
}
do_common_work();
if (different_type)
do_more_work();
*v = r;
}
CodePudding user response:
auto
variable can only infer its type from the initialization expression in C . If you don't want to explicitly specify its type, you can extract the initialization into a separate function which return
s the necessary value (type is auto
) and initialize with this function's call.
In particular, the extracted function can be a lambda expression, so you get an immediately invoked function expression or IIFE:
#include <iostream>
#include <type_traits>
template <bool value, typename...>
struct bool_type : std::integral_constant<bool, value> {};
template <typename T> void function(T *v) {
bool different_type = false;
auto r = [&] { // create a function
if constexpr (std::is_same_v<T, int>) {
return 10;
} else if constexpr (std::is_same_v<T, double>) {
return 10.0;
} else if constexpr (std::is_same_v<T, long>) {
different_type = true;
return 10LL;
} else {
static_assert(bool_type<false, T>::value, "Unsupported type");
}
}(); // immediately invoke the created function
std::cout << typeid(r).name() << " " << different_type << "\n";
*v = r;
}
int main() {
int a;
double b;
long c;
[[maybe_unused]] float d;
function(&a); // int 0
function(&b); // double 0
function(&c); // long long 1
// function(&d); // compilation error
}
In the code above, the lambda expression has a return type of auto
, i.e. it's automatically deduced from the return
chosen by if constexpr
. Only a single return is chosen, so the return type is unambiguous, so r
's type is also inferred correctly.