Is template metaprogramming fully able to be substituted in C++20?
Asked Answered
M

1

12

Although the idea of template metaprogramming - calculate something at compile time when possible - is wonderful,I wonder if current C++20 features allow us to avoid the TMP fully by using constexpr, consteval, if constexpr,concept, and other C++20 features. Is this true? Or some functionality that TMP offer is not able to be substituted?

Moyer answered 9/2, 2022 at 2:11 Comment(6)
No, it is not able. float/double types can not be template typed parameters, so the floating point arithmetic is not possible in template metaprogramming.Uxmal
TMP can produce types. For example, how do you return a tuple<int, int, std::string> from parse("%d-%d: %s", str)?Frederiksberg
@Frederiksberg Maybe that is the answer. Sorry I cannot choose the answer since this post is closed.Moyer
@K.R.Park, It is an idea, but it's not very comprehensive. There's lots of existing practice out there to look through (Boost over the years comes to mind as a bunch of good candidates) and consider what parts could change with C++20. Quite frankly, I could see a comprehensive look at this taking up pages and pages of space. For one example in particular, I'd suggest you get familiar with Boost.Hana, as it's built to bridge the gap between types and values, but requires a lot of templates in the implementation.Frederiksberg
@273K: Floating point are allowed in C++20 as non-type template_parameters.Vav
Concepts simplify some SFINAE code detecting compile-time properties of types while improving upon them by being easier to read, maintain and debug, but they don't fully replace them. For example, concepts can't be specialized explicitly or partially, seemingly for good reasons.Bronwynbronx
C
9

No, template metaprogramming cannot be fully replaced by C++20 language utilities; though a large amount can.

constexpr, consteval, etc. all certainly help lighten the load off of things that are traditionally done with TMP (as has been increasingly the case over the years), but templates still serve an orthogonal purpose of type-determination and, more importantly, type-generation.

constexpr/consteval/etc are limited in their expressiveness, since the parameters to such functions are not, themselves, constant expressions. This is true even in consteval functions, despite the fact that this can only run at compile-time. This means that the following is not legal C++:

template <typename T>
consteval auto make_array(std::size_t n) { 
  return std::array<T,n>{}; 
  //                  ^ - error, 'n' is not a constant expression
}

Live example

This limitation means that more complex generation still requires conventional template-metaprogramming techniques. This especially becomes necessary when trying to produce variadic expansion.

(As stated in the comments, this would also prevent generating an appropriate type when parsing a string).

As an example, consider what the implementation of std::index_sequence would look like without using intrinsics. The naive implementation of make_index_sequence<N> which expands into index_sequence<0, 1, 2, ..., N-1>, would look something like this:

template <std::size_t... Ints>
struct index_sequence 
{
  static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
};


namespace detail {
  template <bool End, std::size_t N, std::size_t...Tails>
  struct make_index_sequence_impl
    : make_index_sequence_impl<((N-1) == 0u), N-1, N-1, Tails...>{};

  template <std::size_t N, std::size_t...Tails>
  struct make_index_sequence_impl<true, N, Tails...>
    : std::type_identity<index_sequence<Tails...>>{};

} // namespace detail

template <std::size_t N>
using make_index_sequence = typename detail::make_index_sequence_impl<(N==0u), N>::type;

Live Example

Technically the index_sequence example can be written in C++20 with recursive consteval functions that return an instance of the index_sequence -- but this only works because we can pass in a value to a template-deduced function. More complex type examples would not have this luxury at compile-time.

In general, more complex type-generation like this will require some level of TMP, especially if the type has restrictions on default-constructibility, or can't itself be a constant expression. In such a case, this would require partial template specializations which start to fall under general template-metaprogramming practices.

Cyclist answered 15/2, 2022 at 18:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.