C ++, which can only be used as a constexpr variable

I have a library type that should only be used as a global variable and should be initialized by the linker (i.e. it should have the correct initial values ​​before the static initialization time). I have good reason to think that I will get what I need if I do one of two things:

  • make it a POD type , first place the user-provided elements and trust the user to pass the correct number of expressions to the syntax Type var = { expr, expr }; .
  • make the implementation private, provide the constexpr constructor and depend on the user to declare all instances as constexpr .

None of them are good in that it depends on the fact that the user has not mixed things up.

The magic of magic is ending, is there a way to forcefully ensure that all instances of the type are constexpr ?

+6
source share
2 answers

Create

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

And if your type provides only the const method, the user should use a const object.

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

myobject is const from a constexpr .

0
source

What you cannot prevent are users declaring const& for all instantiated instances. However, you can prevent copying and moving instances. Now you only need to ensure that all instantiated instances are created in a context where a constant expression is required.

Here's a weird way to provide this: let all instances be static constexpr members of a class (template).

The user then provides a way to get the constructor parameters of your "variable".

 struct constructor_params { int i; double d; }; 

The instance provided by the user must be used in a constant expression to initialize the static constexpr member.

To create different instances, we need some kind of tag value to create different instances of the class template containing the static constexpr element, which will serve as an instance of the variable. I decided to combine the tag value and the method of supplying the constructor_params parameter, giving the user the opportunity to use a function or factory type to create the parameter.

First, for a variable type, you only want to have constexpr instances:

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

So that the user can access two ways to create a variable with the same name, there is an overloaded make_variable function:

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

Now two examples of use. One with the constexpr function to create constructor_params and one with a local type (the scope is the reason for creating from the type).

 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>(); } 
0
source

All Articles