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