Keep It Tight: Restrict Variadic Templates Using C++20 Concepts

I previously wrote about C++20's Concepts. Today I would like to discuss another aspect of Concepts: how they help you to limit the data type of a variadic type-template parameter to a specific one for all datatypes in the set.

For reference, these are my previous posts about C++20 Concepts:

Suppose you have a function template Sum which can take an arbitrary number of parameters, which values the function sums together and returns the result. An implementation using C++17's fold expression looks like this:

1
2
3
4
5
template<typename T, typename... Ts>
auto Sum(const T& val, const Ts&... vals)
{
  return (val + ... + vals);
}

The implementation works, but it leaves the door wide open for using Sum with mixed data types:

1
2
Sum(2, 3, 4, 5);    A 
Sum(2, 3, 4, 5.6);  B 

I'm happy with A; but I don't want B to compile. Sticking with C++17, I can limit Sum using a static_assert and type traits:

1
2
3
4
5
6
7
template<typename T, typename... Ts>
auto Sum(const T& val, const Ts&... vals)
{
  static_assert((std::is_same_v<T, Ts> and ...));

  return (val + ... + vals);
}

One issue with this solution is apparent: users of Sum are unaware of the restriction unless they look into the implementation details. Which they shouldn't. Documentation can help, of course, but the aim is to keep as much documentation as possible in the code itself.

Book an in-house C++ training class

Do you like this content?

Here is your next chance joining my class:
  • Modern C++: When Efficiency Matters @CppCon
  • September 09 - 11, 2026, - UTC
Book your seat now!

There could be another issue if you were solving some different situation. What if you want to allow overloads? With the static_assert that way is hard blocked. For the case I present here, that might be okay. But even here, what if you intend to allow an overload for mixed floating-point types?

Concepts to the rescue

If you add concepts to the table, a solution can look like follows:

1
2
3
4
5
template<typename T, std::same_as<T>... Ts>
auto Sum(const T& val, const Ts&... vals)
{
  return (val + ... + vals);
}

I use the concept std::same_as here to limit the data types in Ts to the same type as T. In C++20 - Filling blanks I explained that Concepts have the power to fill in blanks from the left to the right, which is precisely what the compiler does here.

Variadic parameters of the same type

You can even strive for an entirely different task and say that you want a variadic function template for a single specific type, say int.

The Sum implementation would then change:

1
auto Sum(const std::same_as<int> auto&... vals) { return (vals + ...); }

Concepts are a great, powerful tool for your generic programming toolbox.

Andreas

Recent posts