And other than the pi example, how would it work with non-const
variables?
Currently, it seems to instantiate the variables separately for the type. i.e., you could assign 10 to n<int>
and it would be different from the template definition.
template<typename T>
T n = T(5);
int main()
{
n<int> = 10;
std::cout << n<int> << " "; // 10
std::cout << n<double> << " "; // 5
}
If the declaration is const
, it is readonly. If it's a constexpr
, like all constexpr
declarations, it has not much use outside constexpr
(ressions).
Besides that we can have this feature just by wrapping a variable
within a templated struct or class, how does this mix with type
conversions?
It's meant to be a simple proposal. I am unable to see how it affects type conversions in a significant way. As I already stated, the type of the variable is the type you instantiated the template with. i.e., decltype(n<int>)
is int. decltype((double)n<int>)
is double and so on.
Any usage example to understand how to make the most of such a feature
and what its purpose is?
N3651 provides a succinct rationale.
Alas, existing C++ rules do not allow a template declaration to
declare a variable. There are well known workarounds for this
problem:
? use constexpr static data members of class templates
? use constexpr function templates returning the desired values
These workarounds have been known for decades and well documented.
Standard classes such as std::numeric_limits are archetypical
examples. Although these workarounds aren’t perfect, their drawbacks
were tolerable to some degree because in the C++03 era only simple,
builtin types constants enjoyed unfettered direct and efficient
compile time support. All of that changed with the adoption of
constexpr variables in C++11, which extended the direct and efficient
support to constants of user-defined types. Now, programmers are
making constants (of class types) more and more apparent in programs.
So grow the confusion and frustrations associated with the
workarounds.
...
The main problems with "static data member" are:
? they require "duplicate" declarations: once inside the class
template, once outside the class template to provide the "real"
definition in case the con- stants is odr-used.
? programmers are both miffed and confused by the necessity of providing twice the same
declaration. By contrast, "ordinary" constant declarations do not need
duplicate declarations.
...
Well known examples in this category are probably static member
functions of numeric_limits, or functions such as
boost::constants::pi<T>()
, etc. Constexpr functions templates do not
suffer the "duplicate declarations" issue that static data members
have; furthermore, they provide functional abstraction. However, they
force the programmer to chose in advance, at the definition site, how
the constants are to be delivered: either by a const reference, or by
plain non- reference type. If delivered by const reference then the
constants must be systematically be allocated in static storage; if
by non-reference type, then the constants need copying. Copying isn’t
an issue for builtin types, but it is a showstopper for user-defined
types with value semantics that aren’t just wrappers around tiny
builtin types (e.g. matrix, or integer, or bigfloat, etc.) By
contrast, "ordinary" const(expr) variables do not suffer from this
problem. A simple definition is provided, and the decision of
whether the constants actually needs to be layout out in storage only
depends on the usage, not the definition.