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

C++TemplatesC++14

C++ Problem Overview


C++14 will allow the creation of variables that are templated. The usual example is a variable 'pi' that can be read to get the value of the mathematical constant π for various types (3 for int; the closest value possible with float, etc.)

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? I see some overlapping.

And other than the pi example, how would it work with non-const variables? Are there any usage examples to understand how to make the most of such a feature and what its purpose is?

C++ Solutions


Solution 1 - C++

> 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.

Solution 2 - C++

> we can have this feature just by wrapping a variable within a templated struct or class

Yes, but that would be gratuitous syntactic salt. Not healthy for the blood pressure.

pi<double> conveys the intent better than pi<double>::value. Short and to the point. That's enough of a reason in my book to allow and encourage this syntax.

Solution 3 - C++

Another practical example for C++14's variable templates is when you need a function for passing something into std::accumulate:

template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;

std::accumulate(some.begin(), some.end(), initial, maxer<float>);

Note that using std::max<T> is insufficient because it can't deduce the exact signature. In this particular example you can use max_element instead, but the point is that there is a whole class of functions that share this behavior.

Solution 4 - C++

I wonder whether something along these lines would be possible: (assuming availability of template lambdas)

void some_func() {
    template<typename T>
    std::map<int, T> storage;

    auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };

    store(0, 2);
    store(1, "Hello"s);
    store(2, 0.7);

    // All three values are stored in a different map, according to their type. 
}

Now, is this useful?

As a simpler use, notice that the initialization of pi<T> uses explicit conversion (explicit call of a unary constructor) and not uniform initialization. Which means that, given a type radians with a constructor radians(double), you can write pi<radians>.

Solution 5 - C++

Well, you can use this to write compile time code like this:

#include <iostream>

template <int N> const int ctSquare = N*N;

int main() {
	std::cout << ctSquare<7> << std::endl;
}

This is a significant improvement over the equivalent

#include <iostream>

template <int N> struct ctSquare {
	static const int value = N*N;
};

int main() {
	std::cout << ctSquare<7>::value << std::endl;
}

that people used to write to perform template metaprogramming before variable templates were introduced. For non-type values, we were able to do this since C++11 with constexpr, so template variables have only the advantage of allowing computations based on types to the variable templates.

TL;DR: They don't allow us to do anything we couldn't do before, but they make template metaprogramming less of a PITA.

Solution 6 - C++

I have a use case here.

template<typename CT> constexpr CT MARK = '%';
template<> constexpr wchar_t MARK<wchar_t> = L'%';

which are used in a string processing template.`

template <typename CT> 
void ProcessString(const std::basic_string<CT>& str)
{
    auto&& markpos = str.find(MARK<CT>);
    ...
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjbgsView Question on Stackoverflow
Solution 1 - C++user1508519View Answer on Stackoverflow
Solution 2 - C++n. 1.8e9-where's-my-share m.View Answer on Stackoverflow
Solution 3 - C++Levi MorrisonView Answer on Stackoverflow
Solution 4 - C++Laurent LA RIZZAView Answer on Stackoverflow
Solution 5 - C++cmaster - reinstate monicaView Answer on Stackoverflow
Solution 6 - C++Tiger HwangView Answer on Stackoverflow