在编译时延迟选择类型

deferred selection of types during compile-time

有没有一种标准方法可以让我在 c++20 的编译时 select 一个类型,当该类型依赖于函数后面可用的编译时信息时,即该类型是“延迟的” " 因为中间编译时依赖性。

例如像这样依赖于 auto 关键字但不编译的东西:

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;
}

或者此示例依赖于 static if 提案,该提案阻止 if constexpr 条件语句创建新范围。该提案未通过,因此代码无法编译。

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;
}

auto 变量只能从 C++ 中的初始化表达式推断其类型。如果你不想明确指定它的类型,你可以将初始化提取到一个单独的函数中,其中 returns 必要的值(类型是 auto)并用这个函数的调用初始化。

特别是,提取的函数可以是 lambda 表达式,所以你得到 an immediately invoked function expression 或 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
}

在上面的代码中,lambda 表达式的 return 类型为 auto,即它是根据 if constexpr 选择的 return 自动推导出来的。只选择了一个 return,所以 return 类型是明确的,所以 r 的类型也被正确推断。