I have a library type that is only supposed to be used as a global variable and must be linker initialized (i.e. it must have the correct initial values before static initialization time). I have strong reason to think that I will get what I need if I do one of two things:
Type var = { expr, expr };
syntax.constexpr
constructor and depend on the user to declare all instances as constexpr
.Neither of these is nice in that it depends on the user not mucking things up.
Short of macro magic, is there any way to enforce that all instances of a type be constexpr
?
One or more parameters, each of which must be a literal type and must itself be a constant expression. A constexpr variable or function must return a literal type. The primary difference between const and constexpr variables is that the initialization of a const variable can be deferred until run time.
C++ 14 allows more than one statement. constexpr function should refer only to constant global variables. constexpr function can call only other constexpr function not simple function. The function should not be of a void type and some operators like prefix increment (++v) are not allowed in constexpr function.
A constexpr function or constructor is implicitly inline. The following rules apply to constexpr functions: A constexpr function must accept and return only literal types. A constexpr function can be recursive. It cannot be virtual. A constructor cannot be defined as constexpr if the enclosing class has any virtual base classes.
A constexpr function is one whose return value is computable at compile time when consuming code requires it. Consuming code requires the return value at compile time to initialize a constexpr variable, or to provide a non-type template argument. When its arguments are constexpr values, a constexpr function produces a compile-time constant.
Create a
template<Type1 value1, Type2 value2>
constexpr MyType make_MyTYpe(/* No args */)
{
return MyType(value1, value2); // call the (private) constexpr constructor
}
And if your type provide only const method, user have to use const
object.
const MyType myobject = make_MyType<4, 2>();
myobject
is const
from a constexpr
.
What you cannot prevent is users declaring const&
to any instances created. However, you can prevent copying and moving instances. Now you'll only need to enforce that all instances created are created in a context where a constant expression is required.
Here's a weird way to enforce 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 has to be usable in a constant expression in order to initialize the static constexpr
member.
In order to create different instances, we need some kind of tag value to create different instantiations of a class template that contains the static constexpr
member that'll serve as variable instance. I chose to combine the tag value and the way of supplying the constructor_params
parameter by letting the user provide a factory function or type to create the parameter.
First, the variable type you want only to have constexpr
instances of:
// 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;
};
In order to let the user access both ways to create a variable with one 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 usage examples. One with a constexpr
function to create the constructor_params
and one with a local type (the function scope is the reason why the creation from type is necessary).
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>();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With