C++14 Variable Templates: What Is Their Purpose? Any Usage Example

C++14 Variable Templates: what is their purpose? Any usage example?

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.

C++14 Variable Templates

One of the properties of templates is support of explicit (and partial) specializations. I'd guess that this would apply to variable templates as well, allowing you to provide individual initializers for different specializations, as in

template<typename T>
constexpr T pi = T(3.1415926535897932385);

template<>
constexpr float pi = 3.1415;

template<>
constexpr MyFractionType pi = MyFractionType(22, 7); // close enough for most purposes

template<>
constexpr int pi = 3; // :)

As it's been mentioned in the comments, it is already possible to "templatize and specialize variables" by wrapping them into a class (as static members). Variable templates would allow one to do that without resorting to a class-wrapper-based workaround. In that sense, template variables solve the same issues as template typedefs do.

What exactly is a variable template

A class template is a template that defines a series of classes, based on one or more template parameters. A function template is a template that defines a series of functions, based on one or more template parameters. vector is a template class; vector<int> is a specific class instantiated from that template.

A variable template is therefore a template that defines a series of variables, based on one or more template parameters:

template<typename T>
T variable_name{};

That is a variable template. You would specify which one you want just like any other template: variable_name<int> will be of type int. variable_name is a template; variable_name<int> is actually a variable.

Of course, you can do more complex things:

template<typename T>
vector<T> vector_var{};

vector_var<int> is a vector<int>.

Variable templates can only be introduced at namespace/global scope and as static members of classes. At namespace/global scope, they're usually declared constexpr (and/or in C++17, inline). They're useful for making constants:

template<typename T>
inline constexpr bool is_default_constructible_v = std::is_default_constructible<T>::value;

So if you want to tell if something is default constructible, you don't need the slightly awkward ::value syntax.

Variable templates are a C++14 feature, which is why your compiler probably warned you about using them in a C++11 mode.


image<T> *im = new image<T>(w, h, false);

This is not a variable template. This is a regular variable whose type is very well defined: image<T>. There is exactly one variable named "im". You don't use im<T> to get a variable.

That statement does not define a family of variables. What you have is a family of functions, where each of them contain a variable called im. The function is the template here, not the variable.

Variable template type

There is no requirement the type of a template variable differs based on template arguments.

A common example is std::is_same_v<T,U> which is always a bool.

How should a Variable Template be referred to in C++14 when declared at Class scope?

I copied your first code into Visual Studio 2015 (it compiled successfully). I added a few outputs via std::cout, where I found using b gives a compiler error: uninitialized local variable 'b' used. a on the other hand, was successfully printed when b was not used. So it would seem c++ is a bit finicky about accessing template static members as you said, requiring you to reference it by its complete, qualified name.

Perhaps more curious, are the following lines:

std::cout << example::var<int> << "a\n";

The above line works as expected, outputting 1.5 truncated to 1 and an 'a' with a new line. Nothing to write home about.

std::cout << obj.var<int> << "b\n";

Now here's where it gets interesting... Not only does the above line not print out a value for obj.var<int>, the 'b'\n never gets printed either. I even tested against the std::cout's good() fail() and bad() functions, none of which reported that anything was wrong (and further uses of std::cout were executed successfully outputted).

Another oddity I found was that auto x = obj.var is legal, and come to find, x is of type example. Now, doing this with a global template variable results in a compiler error (as I expected the first one to as well):

template<typename T> constexpr T ex = 1.5;
auto x = ex // compiler error: argument list for variable template "ex" is missing

Additionally, I found that access var through another template static function was successful, further implying that member selection just doesn't work in this case

class example
{
public:
template <class T> static constexpr T var = T(1.5);
template <typename T> static void thing()
{
std::cout << var<T> << '\n'; // works
std::cout << example::var<T> << '\n'; // also works
}
};

Now, as far as the standard goes, I'm inclined to believe their phrasing is just a bit ... pedantic. The sections you've quoted from the standard:

it is not necessary to use the class member access syntax (5.2.5) to refer to a static member.

and

A variable template at class scope is a static data member template.

would seem to imply that this would work. I believe the point where these quotes don't apply in this case is the technicality that a template (of anything) doesn't really "exist" until it is instantiated in a compilation unit (i.e. why the code for templates is often included in the header file itself).

Because of this, although the template variable can be a member of a class, its instantiations aren't ... for some reason ... and therefore require the scope resolution operator as opposed to the member selection operator.

But IMO, accessing static data via the member selection operator is bad practice, as static data and functions are not actually part of a given object. Accessing static data the same way as non-static data can cause relatively-innocent looking code to actually be flawed logic. For instance if for some reason you had a non-const static member called something, you could write example_object.something = 42, not expecting anything to change for all other instantiations of that class throughout your program (the same issues as global variables, really). Because of this (and the fact that member access syntax apparently doesn't work for template static member variables anyway), I recommend always using scope resolution to access/modify static content from outside of the class. example_class::something = 42 is a lot more clear that we're changing something for all instances of example_class. In fact, some more-modern languages like C# require you to access static data via the class name unless you're inside said class.

Given that several compilers are erroring for different parts of this little example program, I'd be willing to bet it's not covered very well in the standard (and probably not used very often in practice), and the compilers just handle it differently (another reason to avoid it).

tl;dr

Apparently while member selection syntax works for static member variables, it doesn't work for template static member variables (though the compiler doesn't seem to complain). However, the scope resolution syntax does work, and IMO should be preferred anyway.

What are some uses of template template parameters?

I think you need to use template template syntax to pass a parameter whose type is a template dependent on another template like this:

template <template<class> class H, class S>
void f(const H<S> &value) {
}

Here, H is a template, but I wanted this function to deal with all specializations of H.

NOTE: I've been programming c++ for many years and have only needed this once. I find that it is a rarely needed feature (of course handy when you need it!).

I've been trying to think of good examples, and to be honest, most of the time this isn't necessary, but let's contrive an example. Let's pretend that std::vector doesn't have a typedef value_type.

So how would you write a function which can create variables of the right type for the vectors elements? This would work.

template <template<class, class> class V, class T, class A>
void f(V<T, A> &v) {
// This can be "typename V<T, A>::value_type",
// but we are pretending we don't have it

T temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

NOTE: std::vector has two template parameters, type, and allocator, so we had to accept both of them. Fortunately, because of type deduction, we won't need to write out the exact type explicitly.

which you can use like this:

f<std::vector, int>(v); // v is of type std::vector<int> using any allocator

or better yet, we can just use:

f(v); // everything is deduced, f can deal with a vector of any type!

UPDATE: Even this contrived example, while illustrative, is no longer an amazing example due to c++11 introducing auto. Now the same function can be written as:

template <class Cont>
void f(Cont &v) {

auto temp = v.back();
v.pop_back();
// Do some work on temp

std::cout << temp << std::endl;
}

which is how I'd prefer to write this type of code.

C++ templates that accept only certain types

I suggest using Boost's static assert feature in concert with is_base_of from the Boost Type Traits library:

template<typename T>
class ObservableList {
BOOST_STATIC_ASSERT((is_base_of<List, T>::value)); //Yes, the double parentheses are needed, otherwise the comma will be seen as macro argument separator
...
};

In some other, simpler cases, you can simply forward-declare a global template, but only define (explicitly or partially specialise) it for the valid types:

template<typename T> class my_template;     // Declare, but don't define

// int is a valid type
template<> class my_template<int> {
...
};

// All pointer types are valid
template<typename T> class my_template<T*> {
...
};

// All other types are invalid, and will cause linker error messages.

[Minor EDIT 6/12/2013: Using a declared-but-not-defined template will result in linker, not compiler, error messages.]



Related Topics



Leave a reply



Submit