Is there any examples out there where template metaprogramming would be better to use than the new constexpr? From what I've understood, both constexpr and template metaprogramming have similar purposes, yet template metaprogramming is not obsolete. So there has to be some examples where template metaprogramming is preferred over constexpr. Any shared thoughts on this would be highly appreciated, thanks!
constexpr
provides true support for compile-time computing in the form of true C++ functions instead of functional-like template-based constructions (Metafunctions). So partially the answer is yes constexpr beats tmp on compile time computing, at least on its syntax for no fp initiated people used to C++. Note that I'm ignoring concerns about compiler performance etc.
On the other hand, tmp is still relevant, and actually the only way, to do type computing in C++. There are new approaches to improve the horrible tmp syntax, like what Boost.Hana does with template variables. But despite the syntax, is still a functional metalanguage separated from "normal" C++.
From the two common tasks you may ask a C++ compiler for (Besides compiling), playing with the type system generating new types on demand depending on your requisites is something that constexpr cannot achieve, simply because that's not what constexpr is supposed/designed to do.
The fun part is that templates were not supposed to do compile-time computing neither. Even metaprogramming. They were designed as the C++ feature for generic programming. But you know the story, middle 90s "Whoaaa C++ templates are turing complete!", expression templates and blitz++ later, then Alexandrescu and his Loki. And now we have <type_traits>
and a serious proposal with a full fledged metaprogramming library inside.
Consider this example (Not mine, taken from this Eric Niebler "challenge"): Write an utility that gives you the common type between a set of types:
namespace m = ranges::meta;
namespace ml = ranges::meta::lazy;
template<typename T, typename U>
using builtin_common_t =
decltype(true? std::declval<T>() : std::declval<U>());
template<typename T, typename U>
using lazy_builtin_common_t =
m::defer<builtin_common_t, T, U>;
template<typename...Ts>
struct common_type
{};
template<typename ...Ts>
using common_type_t = m::eval<common_type<Ts...>>;
template<typename T>
struct common_type<T>
: std::decay<T>
{};
template<typename T, typename U>
struct common_type<T, U>
: m::if_c<
( std::is_same<decay_t<T>, T>::value &&
std::is_same<decay_t<U>, U>::value ),
ml::let<lazy_builtin_common_t<T, U>>,
common_type<decay_t<T>, decay_t<U>>>
{};
template<typename T, typename U, typename... Vs>
struct common_type<T, U, Vs...>
: ml::let<ml::fold<m::list<U, Vs...>, T, m::quote<common_type_t>>>
{};
As you can see, this problem is about types. Something that constexpr is not supposed to do.
About the challenge, Eric asked both Louis Dionne (Boost.Hana author) and I to write common_type<Ts...>
using our libraries. Code above is Eric's implementation using his Meta library. Sincerely, I cannot beat Louis's fold + maybe monad solution :)
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