gndrive
gndrive

Reputation: 41

Using C++20 concept/requires for function partial specialization

I'm learning C++20 concept/requires. I would like to implement a function for getting the sum of an integer list with template meta programming:

sum<IntList<1, 2, 3>>();

As it's known that C++ standards don't allow function partial specialization, I would like to use C++20 concept/requires to the similar stuff as function partial specialization.

Here is my code:

#include <iostream>

template<int...N>
class IntList;

template<int...N>
concept IsIntList = IntList<N...>{};

template<typename T>
int sum() {
    return 0;
}

template<int...N>
requires IsIntList<N...>
int sum() {
    return (N + ...);
}

int main() {
    std::cout << sum<IntList<1, 2>>() << std::endl;
    return 0;
}

But it could not produce what I want. Put my code in C++ Insights. The first sum is instantiated, instead of the second sum.

Here is the result of C++Insights:

#include <iostream>

template<int...N>
class IntList;

template<int...N>
concept IsIntList = IntList<N...>{};

template<typename T>
int sum() {
    return 0;
}

/* First instantiated from: insights.cpp:21 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
int sum<IntList<1, 2> >()
{
  return 0;
}
#endif


template<int...N>
requires IsIntList<N...>
int sum() {
    return (N + ...);
}

int main()
{
  std::cout.operator<<(sum<IntList<1, 2> >()).operator<<(std::endl);
  return 0;
}

What's the correct way to solve this problem? Thanks!

Upvotes: 4

Views: 1212

Answers (2)

康桓瑋
康桓瑋

Reputation: 42776

Your definition of the concept of IsIntList is wrong, it only evaluates the value of IntList<N...>{}, and since IntList is not a bool type, IsIntList always return false.

You should use template partial specialization to define the IsIntList.

template<int...N>
class IntList;

template<class T>
inline constexpr bool IsIntList = false;
template<int...N>
inline constexpr bool IsIntList<IntList<N...>> = true;

For a specialized version of the sum, just constrain IsIntList<T> to be true, then you can extract the value of the IntList with the help of a tag class and template lambda to calculate the sum.

template<class>
struct tag{};

template<class T>
  requires IsIntList<T>
int sum() {
  return []<int...N>(tag<IntList<N...>>) 
    { return (N + ... + 0); }(tag<T>{});
}

Demo.

Upvotes: 0

Nicol Bolas
Nicol Bolas

Reputation: 473437

The central problem you're encountering is that you have a type template parameter whose type you want to be constrained to being some specialization of some template. That's not a thing you can do with a requires clause. At least, not easily.

It's best to avoid this problem. You're only encountering it because you insist that sum's template parameter must be some specialization of IntList instead of the integers themselves directly. The best way to handle this is by ditching this assumption:

template<int... Ints>
constexpr int sum(IntList<Ints...>)
{ return (0 + ... + Ints); }

You then call this function as so: sum(IntList<1, 2>{}). Note that IntList needs to have a constexpr default constructor.

Upvotes: 3

Related Questions