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

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!

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.

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.

Related

How to get arguments from Variadics Template

This is a good answer already, however, they passed arbitrary numbers of arguments as the parameters when I tried to do the same with arguments as typename (don't know the exact word) like this for example:
int sum=0;
int func()
{
return sum;
}
template <int first, int ... rest>
int func()
{
sum += first;
return func(rest...); //Error C2660 'func': function does not take 4 arguments
/*
return func<rest...>(); this also doesn't work: Error 'int func(void)': could not deduce template argument for 'first' and 'func': no matching overloaded function found
*/
}
int main()
{
cout << func<1,2,3,4,5>();
}
Why there's an error there? Are there any possible fixes? Also, I'm required to pass the argument as typenames, not parameters.
First of all, the "base" function also needs to be a template.
Then to differentiate the two templates, the parameter-pack template needs to take at least two template arguments.
And lastly, you can solve this without the global sum variable, but using the addition in the return statement.
Putting it all together:
template <int first>
int func()
{
return first;
}
template <int first, int second, int ...rest>
int func()
{
return first + func<second, rest...>();
}
For a simple use case as a sum of values, there are many ways (as in Some programmer dude and P Kramer answers).
I'll show you a variant of the recursive solution (Some programmer dude's answer) that isn't particular useful in this case but can come useful in other circumstances (avoiding code repetition when the ground function make something consistent and equal to the recursive version): instead of a ground case version that receive and manage the last value, a ground version that receive a defaulted type and do nothing (return zero, in the sum case):
template <typename = void>
int func ()
{ return 0; }
so you can write the recursive version as follows, without second
template <int first, int ... rest>
int func ()
{ return first + func<rest...>(); }
The trick is in the recursive call: func<rest...>()
When the ...rest template pack isn't empty, the recursive call func<rest...>() invoke the recursive func().
But when the ...rest template pack is empty, the recursive call func<rest...>() become func<>() that doesn't matches the recursive func() (at least a template integer value is required) but matches (thanks to the default void type) func<void>(), so the ground func() function.
If you want to return the sum of a parameter pack and you're okay with a slightly different syntax, you can use fold expressions from C++17 onward or build up recursion with 'if constexpr'
template<typename... args_t>
static constexpr auto sum(args_t&&... values)
{
return (values + ...);
}
// the sum is evaluated at compile time
static_assert(sum(1, 2, 3, 4) == 10ul);
// or like this without fold
template<int value, int... values>
constexpr int sum2()
{
if constexpr (sizeof...(values) > 0)
{
return value + sum2<values...>();
}
else
{
return value;
}
};
static_assert(sum2<1,2,3,4>() == 10);
Just for curiosity here is the version without recursion:
// C++11
template<typename... Ts>
void dummy(Ts... t)
{
}
template<typename T>
T addValue(T& sum, T value)
{
sum += value;
return 0;
}
template<typename SumType, typename... Ts>
SumType sumVariadicArgs(Ts... t)
{
SumType sum = 0;
dummy(addValue(sum, t)...);
return sum;
}
int main()
{
int sum = sumVariadicArgs<int>(1, 2, 3);
double sum2 = sumVariadicArgs<double>(1.0, 2.0, 3.0);
return 0;
}

Return different types with different template parameter value (but same type)

What I want to do is to define 3 functions like these:
template<int t = 0> int test() { return 8; }
template<int t = 1> float test() { return 8.8; }
template<int t = 2> std::string test() { return "8.9"; }
int main()
{
int a = test<0>();
float b = test<1>();
std::string c = test<2>();
return 0;
}
They use the same type of template parameter but return different types.
I believe there must be some way to do that (like std::get<>() does it), but I can not find how to do it.
It looks to me like you are after function template specialization. Needing to provide a different implementation for each of the calls fits the bill. There is however one caveat, and it's that a specialization may not alter the signature of the primary template being specialized, only the implementation. This means we cannot do e.g.
template<int t> int test(); // Primary
template<> int test<0>() { return 8; } // OK, signature matches
template<> float test<1>() { return 8.8; } // ERROR
But we are not toasted yet. The signature of the specialization has to match the one that primary will get for a specific argument. So if we make the return type dependent on the template parameter, and resolve to the correct type, we could define our specialization just fine.
template<int t> auto test() -> /* Magic involving t that resolves to int, float, string */;
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
Does something like that exist? Yes, and you hinted at it. We can use std::tuple. It has a std::tuple_element utility that can map an integer to one of a type sequence (the tuple's elements). With a little helper, we can construct our code to work the way you wish:
using types = std::tuple<int, float, std::string>;
template<int t> auto test() -> std::tuple_element_t<t, types>;
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
Now every specialization matches the signature the primary would end up with. And so we get an approval from our compiler.
See it live
The #StoryTeller and #formerlyknownas_463035818 have provided a well-explained template specialization way of doing it. Alternatively, one can combine the three functions to one single function using if-constexpr and with a decltype(auto) return in c++17.
#include <iostream>
#include <string>
#include <cstring>
using namespace std::literals;
template<int t>
constexpr decltype(auto) test() noexcept
{
if constexpr (t == 0) return 8;
else if constexpr (t == 1) return 8.8f;
else if constexpr (t == 2) return "8.9"s;
}
(See live online)
You declared the same template 3 times, while you actually want specialisations. As far as I know you cannot specialize on return type directly (*). However, there is nothing that cannot be solved with an additional layer of indirection. You can do something along the line of:
#include <string>
template <int> struct return_type_tag {};
template <> struct return_type_tag<0> { using type = int; };
template <> struct return_type_tag<1> { using type = float; };
template <> struct return_type_tag<2> { using type = std::string; };
template <int x> typename return_type_tag<x>::type test();
template<> int test<0>() { return 8; }
template<> float test<1>() { return 8.8; }
template<> std::string test<2>() { return "8.9"; }
int main()
{
int a = test<0>();
float b = test<1>();
std::string c = test<2>();
return 0;
}
(*) actually you can, with a small trick, see this answer. The only benefit of my approach is that it already worked pre-c++11.

C++ Template Specialization with non-generic arguments

i have a quiestion regarding template specializations when i have multiple arguments and some of them are non generic
I have a class called Polinom(polynomian) which is basically an array of T objects with a size declared in the template definition(as an int). I also have a class called Complex. Now what I want is to create a template specialization so that I will call different functions(overloaded operator functions) when I have a polinomial with Complex as T. My question is how can I do this, I am getting an error "type name not allowed, stepen undefined" when trying to create the specialization, if someone could clear up what is going on here that would be great.
#pragma once
#include <iostream>
#include <cmath>
using namespace std;
template<class T, int stepen>
class Polinom
{
private:
T koeficijenti[stepen];
public:
Polinom() {};
~Polinom() {};
void ucitajKoef();
T vrednost(T x);
};
template<>
class Polinom<Complex, int stepen> //error type name not allowed
{
};
template<class T, int stepen>
T Polinom<T, stepen>::vrednost(T x)
{
T suma = koeficijenti[1] * (T)pow(x, stepen);
for (int i = 1; i < stepen; i++)
{
suma += koeficijenti[i] * pow(x, stepen - i);
}
return suma;
}
template<class T, int stepen>
void Polinom<T, stepen>::ucitajKoef()
{
T uneseniKoef;
for (int i = 0; i < stepen; i++)
{
cout << "Unesite koeficijent" << endl;
cin >> uneseniKoef;
koeficijenti[i] = uneseniKoef;
}
}
P.S. Is there a workaround to having to rewrite all the other functions of the class since I am only specializing so I can overload the function vrednost(value)
The specialization should look like:
#include <iostream>
template <typename T, int N>
struct Array {
T var[N];
T sum() {
T res{};
for (T v:var) {
res += v;
}
return res;
}
};
struct Complex {};
template <int N>
struct Array<Complex, N> {
Complex var[N];
Complex sum() {
std::cout <<"Complex sum called\n";
return {};
}
};
int main()
{
Array<Complex,5> array;
array.sum();
}
Note that this is a minimal (I think), complete, and verifiable example. You will get better answers if you take the time to create something similar when you ask a question.
To answer your question: I know of no way to simply specialize just one function in a template - which is why you get complex things like the CRTP and individual functions which are invoked from the members (and which can be individually specialized).

how to do an if else depending type of type in c++ template? [duplicate]

This question already has answers here:
using std::is_same, why my function still can't work for 2 types
(4 answers)
Closed 2 years ago.
// template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase () {
//if(T.type==int)//how to do this or something similar?
//do this if an int
return ++element;
//if(T.type==char)
//if ((element>='a')&&(element<='z'))
//element+='A'-'a';
//return element;
}
};
I know how to write a template specialization and do a separate whole class def just for the char type.
But what if I wanted to handle everything in just one block of code?
How can I check if T is an int or a char?
You could use typeid:
if (typeid(T) == typeid(int))
Or you could use the std::is_same type trait:
if (std::is_same<T, int>::value)
What you want is probably something like a compile-time if.
Unfortunately, C++11 has no native support for such a language construct.
However, if you just want to check whether two types are identical, the std::is_same<> type trait should help you:
#include <type_traits> // <== INCLUDE THIS STANDARD HEADER
// class template:
template <class T>
class mycontainer
{
T element;
public:
mycontainer (T arg) {element=arg;}
T increase ()
{
if (std::is_same<T, int>::value) // <== THIS IS HOW YOU WOULD USE IT
return ++element;
if (std::is_same<T, char>::value) // <== THIS IS HOW YOU WOULD USE IT
{
if ((element>='a') && (element<='z'))
element+='A'-'a';
}
return element;
}
};
However, keep in mind that the condition is evaluated at run-time, even though the value of is_same<T, int>::value is known at compile-time. This means that both the true and the false branch of the if statement must compile!
For instance, the following would not be legal:
if (std::is_same<T, int>::value)
{
cout << element;
}
else if (std::is_same<T, my_class>::value)
{
element->print(); // Would not compile when T is int!
}
Also, as Xeo correctly pointed out in the comments, the compiler will likely issue warnings because your condition will always evaluate to true or to false, so one of the two branches will contain unreachable code.
You may use explicit template specialization
#include <iostream>
using namespace std;
// class template:
template <class T>
class mycontainer {
T element;
public:
mycontainer (T arg) {element=arg;}
T increase();
};
template<>
int mycontainer<int>::increase(){
return ++element;
}
template<>
char mycontainer<char>::increase(){
if ((element>='a')&&(element<='z'))
element+='A'-'a';
return element;
}
int main(){
mycontainer<int> A(10);
mycontainer<char> B('x');
cout << A.increase() <<endl;
cout << B.increase() <<endl;
return 0;
}
How about a simple overload?
// in the private section
static int& do_increase(int& i){ return ++i; }
static char& do_increase(char& c){
if(c >= 'a' && c <= 'z')
c += 'A' - 'a';
return c;
}
template<class U>
static U& do_increase(U& arg){
// some default implementation?
return arg;
}
(Note that the standard doesn't guarantee alphabetic order for the numeric values of a char.)
Then simply call that in increase as return do_increase(element);.
The usual solution here is to forward to an overloaded function
with an additional argument. Something like:
template <typename T>
class MyContainer
{
T increase( int const* ) { /* special treatment for int */ }
T increase( ... ) { /* default treatment */ }
public:
T increase()
{
return increase( (T const*)0 );
}
};
With a little imagination, you can come up with all sorts of
distinctions. If you make the target functions with the extra
arguments templates, you can even leverage off SFINAE: design
the dummy argument so that template type substitution fails, and
the function will not be considered in the overload set. And
since all of the functions are inline, it's probable that there
will be no extra overhead, provided that you optimize.
This is along the lines of Andy Prowls answer but is all done at compile-time using a minimal helper class with specialization.
In this instance you have a helper that actually does the specialization but you could also have the helper class just take a bool and then use something like std::is_same<T, int>::value to pass that value as a template parameter.
template <typename T>
struct myContainerHelper;
{
// General Case
static inline T increase(T element)
{
return ++element;
}
};
template <>
struct myContainerHelper<char>
{
// Specific case
static inline char increase(char element)
{
if ((element>='a')&&(element<='z')) element+='A'-'a';
return element;
}
};
template <class T>
class mycontainer
{
T element;
public:
mycontainer (T arg) {element=arg;}
T increase ()
{
return myContainerHelper<T>::increase(element);
}
};
This allows you to only specialize the single function instead of the entire class. I'm using a template class with statics because I'm used to VS2012 limitations with partial specialization for function templates.

compile time loops

I would like to know if it is possible to have sort of compile time loops.
For example, I have the following templated class:
template<class C, int T=10, int B=10>
class CountSketch
{
public:
CountSketch()
{
hashfuncs[0] = &CountSketch<C>::hash<0>;
hashfuncs[1] = &CountSketch<C>::hash<1>;
// ... for all i until i==T which is known at compile time
};
private:
template<int offset>
size_t hash(C &c)
{
return (reinterpret_cast<int>(&c)+offset)%B;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
I would thus like to know if I can do a loop to initialize the T hash functions using a loop. The bounds of the loops are known at compile time, so, in principle, I don't see any reason why it couldn't be done (especially since it works if I unroll the loop manually).
Of course, in this specific example, I could just have made a single hash function with 2 parameters (although it would be less efficient I guess). I am thus not interested in solving this specific problem, but rather knowing if "compile time loops" existed for similar cases.
Thanks!
Nope, it's not directly possible. Template metaprogramming is a pure functional language. Every value or type defined through it are immutable. A loop inherently requires mutable variables (Repeatedly test some condition until X happens, then exit the loop).
Instead, you would typically rely on recursion. (Instantiate this template with a different template parameter each time, until you reach some terminating condition).
However, that can solve all the same problems as a loop could.
Edit: Here's a quick example, computing the factorial of N using recursion at compile-time:
template <int N>
struct fac {
enum { value = N * fac<N-1>::value };
};
template <>
struct fac<0> {
enum { value = 1 };
};
int main() {
assert(fac<4>::value == 24);
}
Template metaprogramming in C++ is a Turing-complete language, so as long as you don't run into various internal compiler limits, you can solve basically any problem with it.
However, for practical purposes, it may be worth investigating libraries like Boost.MPL, which contains a large number of data structures and algorithms which simplify a lot of metaprogramming tasks.
Yes. Possible using compile time recursion.
I was trying with your code but since it was not compilable here is a modified and compiling exmaple:
template<class C, int T=10>
class CountSketch
{
template<int N>
void Init ()
{
Init<N-1>();
hashfuncs[N] = &CountSketch<C>::template hash<N>;
cout<<"Initializing "<<N<<"th element\n";
}
public:
CountSketch()
{
Init<T>();
}
private:
template<int offset>
size_t hash(C &c)
{
return 0;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
template<>
template<>
void CountSketch<int,10>::Init<0> ()
{
hashfuncs[0] = &CountSketch<int,10>::hash<0>;
cout<<"Initializing "<<0<<"th element\n";
}
Demo. The only constraint of this solution is that you have to provide the final specialized version as, CountSketch<int,10>::Init<0> for whatever type and size.
You need a combination of boost::mpl::for_each and boost::mpl::range_c.
Note: This will result in run-time code and this is what you actually need. Because there is no way to know the result of operator& at compile time. At least none that I'm aware of.
The actual difficulty with this is to build a struct that is templated on an int parameter (mpl::int_ in our case) and that does the assignment when operator() is called and we also need a functor to actually capture the this pointer.
This is somewhat more complicated than I anticipated but it's fun.
#include <boost/mpl/range_c.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/copy.hpp>
// aforementioned struct
template<class C, class I>
struct assign_hash;
// this actually evaluates the functor and captures the this pointer
// T is the argument for the functor U
template<typename T>
struct my_apply {
T* t;
template<typename U>
void operator()(U u) {
u(t);
}
};
template<class C, int T=10, int B=10>
class CountSketch
{
public:
CountSketch()
{
using namespace boost::mpl;
// we need to do this because range_c is not an ExtensibleSequence
typedef typename copy< range_c<int, 0, T>,
back_inserter< vector<> > >::type r;
// fiddle together a vector of the correct types
typedef typename transform<r, typename lambda< assign_hash<C, _1 > >::type >
::type assignees;
// now we need to unfold the type list into a run-time construct
// capture this
my_apply< CountSketch<C, T, B> > apply = { this };
// this is a compile-time loop which actually does something at run-time
for_each<assignees>(apply);
};
// no way around
template<typename TT, typename I>
friend struct assign_hash;
private:
template<int offset>
size_t hash(C& c)
{
return c;
// return (reinterpret_cast<int>(&c)+offset)%B;
}
size_t (CountSketch::*hashfuncs[T])(C &c);
};
// mpl uses int_ so we don't use a non-type template parameter
// but get a compile time value through the value member
template<class C, class I>
struct assign_hash {
template<typename T>
void operator()(T* t) {
t->hashfuncs[I::value] = &CountSketch<C>::template hash<I::value>;
}
};
int main()
{
CountSketch<int> a;
}
with C++20 and consteval compile time loops became possible without doing template hell unless the value can have multiple types:
consteval int func() {
int out = 0;
for(int i = 10; i--;) out += i;
return out;
}
int main() {
std::cout << func(); // outputs 45
}
There are compilers that will see the loop and unroll it. But it's not part of the language specification that it must be done (and, in fact, the language specification throws all sorts of barriers in the way of doing it), and there's no guarantee that it will be done, in a particular case, even on a compiler that "knows how".
There are a few languages that explicitly do this, but they are highly specialized.
(BTW, there's no guarantee that the "unrolled" version of your initializations would be done "at compile time" in a reasonably efficient fashion. But most compilers will, when not compiling to a debug target.)
Here is, I think, a better version of the solution given above.
You can see that we use the compile-time recursive on the function params.
This enables putting all the logic inside your class, and the base case of Init(int_<0>) is very clear - just do nothing :)
Just so you won't fear performance penalty, know that the optimizer will throw away these unused parameters.
As a matter of fact, all these function calls will be inlined anyway. that's the whole point here.
#include <string.h>
#include <stdio.h>
#include <algorithm>
#include <iostream>
using namespace std;
template <class C, int N = 10, int B = 10>
class CountSketch {
public:
CountSketch() {
memset(&_hashFunctions, sizeof(_hashFunctions), 0); // for safety
Init(int_<N>());
}
size_t HashAll(C& c)
{
size_t v = 0;
for(const auto& h : _hashFunctions)
{
v += (this->*h)(c); // call through member pointer
}
return v;
}
private:
template<int offset>
size_t hash(C &c)
{
return (reinterpret_cast<size_t>(&c)+offset)%B;
}
size_t (CountSketch::*_hashFunctions[N])(C &c);
private: // implementation detail
// Notice: better approach.
// use parameters for compile-time recursive call.
// you can just override for the base case, as seen for N-1 below
template <int M>
struct int_ {};
template <int M>
void Init(int_<M>) {
Init(int_<M - 1>());
_hashFunctions[M - 1] = &CountSketch<C, N, B>::template hash<M>;
printf("Initializing %dth element\n", M - 1);
}
void Init(int_<0>) {}
};
int main() {
int c;
CountSketch<int, 10> cs;
int i;
cin >> i;
printf("HashAll: %d", cs.HashAll(c));
return 0;
}
Compiler Explorer