首页 > 解决方案 > 将枚举数组转换为参数包

问题描述

我正在努力创建一个对象,因为我必须parameter pack从函数内的数组创建一个新对象。该数组包含几个enum相同的元素enum type

错误消息指出,该数组不是静态的,因此不是可以转换为参数包的有效输入。

是否有以下可能性:

enum Enums{e1, e2};

template<typename T, auto ...Enums>
class Example{};

template<int size>
constexpr auto createObj(){

    Enums enum_array[size] = {};

    for(int i = 0; i< size ; i++){
        //fill array with e1 and e2
    }

    return Example<double, enum_array>();
}

错误信息:

error: the address of ‘enum_array’ is not a valid template argument because it does not have static storage duration
return Example<double, enum_array>();

标签: c++templatesenums

解决方案


您可能需要一些间接级别。首先,要使用任何值作为模板参数,它必须是编译器的编译时间常数,以便编译器实例化模板并生成其代码。

为此,您需要将计算数组的函数放在 constexpr 函数中:

template<int size>
constexpr auto createObjArray() {
    auto enum_array = std::array<Enums, size>{};

    for(int i = 0; i < size ; i++){
        //fill array with e1 and e2
    }

    return enum_array;
}

然后,您需要一种将数组扩展为包的方法。目前在 C++17(和 C++20)中,它不能在本地完成。您将需要某种索引序列。最直接的方法是使用辅助函数:

template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
    constexpr auto enumArray = createObjArray<sizeof...(S)>();

    return Example<double, enumArray[S]...>();
}

然后从一个面向用户的函数中包装所有这些:

template<int size>
constexpr auto createObj(){
    return createObjHelper(std::make_index_sequence<size>());
}

请注意,其中许多辅助函数可以使用 lambdas 来实现。例如,一个 lambda 可以有一个 constexpr 主体并在编译时在运行时函数内执行:

template<std::size_t... S>
constexpr auto createObjHelper(std::index_sequence<S...>){
    //   implicitly constexpr ---v
    constexpr auto enumArray = []{
        auto enum_array = std::array<Enums, size>{};

        for(int i = 0; i < size ; i++){
            //fill array with e1 and e2
        }

        return enum_array;
    }();

    return Example<double, enumArray[S]...>();
}

在 C++20 中,您可以使用熟悉的 lambda 模板函数语法来避免辅助函数:

template<int size>
constexpr auto createObj(){
    //   implicitly constexpr ---v
    constexpr auto enumArray = []{
        Enums enum_array[size] = {};

        for(int i = 0; i < size ; i++){
            //fill array with e1 and e2
        }

        return enum_array;
    }();

    return []<std::size_t... S>(std::index_sequence<S...>) -> decltype(auto) {
        return Example<double, enumArray[S]...>();
    }(std::make_index_sequence<size>());
}

在 C++23 中,如果批准引入结构化绑定包,则可以删除第二个 lambda,以便直接在函数中创建新包:

template<int size>
constexpr auto createObj(){
    //   implicitly constexpr ---v
    constexpr auto enumArray = []{
        Enums enum_array[size] = {};

        for(int i = 0; i < size ; i++){
            //fill array with e1 and e2
        }

        return enum_array;
    }();

    constexpr auto&& [...allEnums] = enumArray;

    return Example<double, allEnums...>();
}

推荐阅读