C++只能用作 constexpr 变量的类型

C++ type that can only be used as constexpr variable

本文关键字:变量 类型 constexpr C++      更新时间:2023-10-16

我有一个库类型,它只应该用作全局变量,并且必须进行链接器初始化(即它必须在静态初始化时间之前具有正确的初始值(。我有充分的理由认为,如果我做以下两件事之一,我将得到我需要的东西:

  1. 使其成为 POD 类型,将用户提供的成员放在第一位,并信任用户将正确数量的表达式传递给Type var = { expr, expr };语法。
  2. 使实现私有,提供constexpr构造函数,并依赖于用户将所有实例声明为constexpr

这些都不好,因为它取决于用户不会把事情搞砸。

除了宏魔术之外,有没有办法强制constexpr类型的所有实例?

创建一个

template<Type1 value1, Type2 value2>
constexpr MyType make_MyTYpe(/* No args */)
{
    return MyType(value1, value2); // call the (private) constexpr constructor
}

如果您的类型仅提供 const 方法,则用户必须使用const对象。

const MyType myobject = make_MyType<4, 2>();

myobject const constexpr.

您无法

阻止的是用户向创建的任何实例声明const&。但是,您可以阻止复制和移动实例。现在,您只需要强制创建的所有实例都是在需要常量表达式的上下文中创建的。

这是一种奇怪的强制方法:让所有实例都static constexpr类(模板(的成员。

然后,用户提供了一种获取"变量"的构造函数参数的方法。

struct constructor_params
{
    int i;
    double d;
};

用户提供的实例必须在常量表达式中可用,以便初始化static constexpr成员。

为了创建不同的实例,我们需要某种标记值来创建类模板的不同实例化,该类模板包含将用作变量实例的static constexpr成员。我选择结合标签值和提供constructor_params参数的方式,让用户提供工厂函数或类型来创建参数。

首先,您只想包含以下constexpr实例的变量类型:

// forward declarations useful for friendship
template<class T>
struct make_variable_by_type;
template<constructor_params(*fptr)(void)>
struct make_variable_by_func;

struct the_variable
{
    the_variable(the_variable const&) = delete;
    the_variable(the_variable&&) = delete;
private:
    constexpr the_variable(constructor_params) {}
    template<class T>
    friend struct make_variable_by_type;
    template<constructor_params(*fptr)(void)>
    friend struct make_variable_by_func;
};

为了让用户访问两种方式来创建具有一个名称的变量,有一个重载的make_variable函数:

template<constructor_params(*fptr)(void)>
struct make_variable_by_func
{
    static constexpr the_variable value{fptr()};
};
template<constructor_params(*fptr)(void)>
const the_variable make_variable_by_func<fptr>::value;
template<class T>
struct make_variable_by_type
{
    static constexpr the_variable value{T::make()};
};
template<class T>
const the_variable make_variable_by_type<T>::value;

template<class T>
constexpr the_variable const& make_variable()
{
    return make_variable_by_type<T>::value;
}
template<constructor_params(*fptr)(void)>
constexpr the_variable const& make_variable()
{
    return make_variable_by_func<fptr>::value;
}

现在,两个使用示例。一个具有用于创建constructor_paramsconstexpr函数,另一个具有本地类型(函数作用域是需要从类型创建的原因(。

constexpr constructor_params make_my_variable()
{
    return {42, 21.0};
}
constexpr auto& x = make_variable<make_my_variable>();
int main()
{
    struct make_my_other_variable
    {
        static constexpr constructor_params make()
        {
            return {1, 2};
        }
    };
    constexpr auto& x = make_variable<make_my_other_variable>();
}