How to specialize template using operator? - c++

I want to sum the number from 1 to N-1 by TMP. so I wrote a source code as shown below, but an error template argument '(number - 1)' involves template parameter(s) occurred. Do you happen to know how to deal with this?
template <int number, int i = 1>
class Sum {
public:
static const int result = i + Sum<number, i + 1>::result;
};
template <int number>
class Sum<number, number - 1> {
public:
static const int result = number - 1;
};
int main () {
const int result = Sum<10>::result;
return 0;
}

GCC still follows the rule:
Non-type argument expression cannot use the name of the template parameter except when it is exactly the name of the template parameter.
To solve the problem using template specialization, you can wrap the second non-type template parameter into an std::integral_constant to make it a type template parameter:
template<int number, class i = std::integral_constant<int, 1>>
struct Sum {
static constexpr int value = i() + Sum<number,
std::integral_constant<int, i() + 1>>::value;
};
template<int number>
struct Sum<number, std::integral_constant<int, number - 1>> {
static constexpr int value = number - 1;
};
static_assert(Sum<10>::value == 45);
Alternatively, you can use if constexpr and no partial specialization:
template<int number, int i = 1>
struct Sum {
static constexpr int value = [] {
if constexpr (number == i + 1)
return i;
else
return i + Sum<number, i + 1>::value;
}();
};
static_assert(Sum<10>::value == 45);
Yet another way is given by fold expressions:
template<class> struct Sum_impl;
template<int... numbers>
struct Sum_impl<std::integer_sequence<int, numbers...>> {
static constexpr int value = (... + numbers);
};
template<int number>
struct Sum : Sum_impl<std::make_integer_sequence<int, number>> {};

Related

Represents empty type in C++ class template

consider the following example of a compile-time "vector".
#include <iostream>
template <int n, int...ns>
struct static_vector {
static constexpr int value = n;
static_vector<ns...> rest;
};
template <int n>
struct static_vector<n> {
static constexpr int value = n;
void* rest;
};
template <int n, class sv>
constexpr int access_nth() {
static_assert(n >= 0, "vector size out of bound");
if constexpr(n == 0) {
return sv::value;
} else {
static_assert(!std::is_same_v<decltype(sv::rest), void *>, "vector size out of bound");
return access_nth<n-1, decltype(sv::rest)>();
}
}
int main()
{
constexpr auto a = static_vector<12, 23, 34, 45>();
constexpr int nth = access_nth<5, decltype(a)>();
std::cout << nth << std::endl;
}
I am mostly satisfied with what we can do now: define a vector and then get the nth element out of it. The one thing I found that not satisfying is this: I have to use void * as a dummy in the base case (where the vector only holds one element and no tail...)
I tried to have a specialisation like this:
template <>
struct static_vector<> {
}
to represent the empty vector. But it seems that the compiler always rejects this definition with the following error:
<source>:16:8: error: too few template arguments for class template 'static_vector'
struct static_vector<> {
^
What should I do here so I can have an empty vector?
Thanks a lot.
But why recursion ?
You tagged C++17 so you can use template folding, so... what about as follows ?
#include <iostream>
template <int ... Is>
struct static_vector
{
template <std::size_t N>
int get () const
{
static_assert( N < sizeof...(Is), "index out of bound" );
std::size_t i{};
int ret;
( ... , (N == i++ ? ret = Is : 0) );
return ret;
}
};
int main()
{
constexpr auto a = static_vector<12, 23, 34, 45>();
std::cout << a.get<3u>() << std::endl;
}
Specializations must conform to the base template declaration. Since at least one int is required by the base template, this does not compile.
You can make this work by declaring the template to take any number of int arguments, then specializing every case that takes one or more arguments. The base declaration is then the empty case:
template <int...>
struct static_vector {
// Instantiated only for the no-argument case
};
template <int n>
struct static_vector<n> {
// One-argument specialization
};
template <int n, int... ns>
struct static_vector<n, ns...> {
// Two-or-more-argument specialization
};

Template Metaprogramming to calculate Fibonacci

Recently in a job interview I was asked to give the result of 100th element of a 3rd-class Fibonacci sequence(Fib(n)=Fib(n-1)+Fib(n-2)+Fib(n-3). I finished by Mathematical Induction and constructing a class to present numbers larger than long long. Then I was asked to implement it via template meta-programming. The problem is that the result will exceed the range of long long and I don't know how to fix this. Here is my code using template meta-programming.
template<long long num>
struct fib
{
enum { result = fib<num - 1>::result + fib<num - 2>::result + fib<num - 3>::result};
};
template<>
struct fib<0>
{
enum { result = 1 };
};
template<>
struct fib<1>
{
enum { result = 1 };
};
template<>
struct fib<2>
{
enum { result = 2 };
};
template<>
struct fib<3>
{
enum { result = 4 };
};
int main()
{
cout << fib<100>::result << endl;
return 0;
}
A possible implementation is to use a custom structure to store the numbers instead of a built-in type. You could for instance store numbers like this:
template <int... Digits>
struct number<Digits... > { };
Note: For the sake of simplicity when adding, I store the digits in reverse order, so the number 275 is stored as number<5, 7, 2>.
Fibonacci only requires addition, so you simply have to define addition, e.g., a template add (see the end of the answer for the actual implementation).
You can then define the fib template quite easily:
template <int N>
struct fib_impl {
using type = add_t<
typename fib_impl<N-1>::type,
typename fib_impl<N-2>::type,
typename fib_impl<N-3>::type>;
};
template <>
struct fib_impl<0> { using type = number<0>; };
template <>
struct fib_impl<1> { using type = number<0>; };
template <>
struct fib_impl<2> { using type = number<1>; };
template <int N>
using fib = typename fib_impl<N>::type;
And with an appropriate output operator (see below), you can print the 100th Tribonacci number:
int main() {
std::cout << fib<100>{} << "\n";
}
Which outputs:
53324762928098149064722658
While the 100th is not present in the OEIS, you can check that the 37th one is correct:
static_assert(std::is_same_v<fib<37>, number<2, 5, 8, 6, 3, 4, 2, 3, 1, 1>>);
Implementation of operator<<:
std::ostream& operator<<(std::ostream &out, number<>) {
return out;
}
template <int Digit, int... Digits>
std::ostream& operator<<(std::ostream &out, number<Digit, Digits... >) {
// Do not forget that number<> is in reverse order:
return out << number<Digits... >{} << Digit;
}
Implementation of the add template:
This is a small cat utility to concatenate numbers:
// Small concatenation utility:
template <class N1, class N2>
struct cat;
template <int... N1, int... N2>
struct cat<number<N1... >, number<N2... >> {
using type = number<N1... , N2...>;
};
template <class N1, class N2>
using cat_t = typename cat<N1, N2>::type;
The actual implementation of the addition:
template <class AccNumber, int Carry, class Number1, class Number2>
struct add_impl;
template <class AccNumber, int Carry>
struct add_impl<AccNumber, Carry, number<>, number<>> {
using type = std::conditional_t<Carry == 0, AccNumber, cat_t<AccNumber, number<1>>>;
};
template <class AccNumber, int Carry,
int Digit2, int... Digits2>
struct add_impl<AccNumber, Carry, number<>, number<Digit2, Digits2...>> {
using type = typename add_impl<
cat_t<AccNumber, number<(Digit2 + Carry) % 10>>,
(Digit2 + Carry) / 10,
number<Digits2... >, number<>>::type;
};
template <class AccNumber, int Carry,
int Digit1, int... Digits1>
struct add_impl<AccNumber, Carry, number<Digit1, Digits1... >, number<>> {
using type = typename add_impl<
cat_t<AccNumber, number<(Digit1 + Carry) % 10>>,
(Digit1 + Carry) / 10,
number<Digits1... >, number<>>::type;
};
template <class AccNumber, int Carry,
int Digit1, int... Digits1, int Digit2, int... Digits2>
struct add_impl<AccNumber, Carry, number<Digit1, Digits1... >, number<Digit2, Digits2...>> {
using type = typename add_impl<
cat_t<AccNumber, number<(Digit1 + Digit2 + Carry) % 10>>,
(Digit1 + Digit2 + Carry) / 10,
number<Digits1... >, number<Digits2... >>::type;
};
A short wrapper:
template <class... Numbers>
struct add;
template <class Number>
struct add<Number> {
using type = Number;
};
template <class Number, class... Numbers>
struct add<Number, Numbers... > {
using type = typename add_impl<
number<>, 0, Number, typename add<Numbers... >::type>::type;
};
template <class... Numbers>
using add_t = typename add<Numbers... >::type;
I am not aware of ready-to-use arbirtrary precicion facitlities for templates. However, a toy number-type that can hold numbers bigger than long long is easy to write:
template <long long H,long long L>
struct my_number {
static const long long high = H;
static const long long low = L;
static const long long mod = 10000000000;
static void print() {
std::cout << high << setw(10) << setfill('0') << low;
}
};
It stores the last 10 digits of the result in low and the leading digits in high. Two my_numbers can be summed via
template <typename A,typename B>
struct sum {
static const long long low = (A::low + B::low) % A::mod;
static const long long high = A::high + B::high + (A::low + B::low) / A::mod;
using number = my_number<high,low>;
};
and for 3 numbers:
template <typename A,typename B,typename C>
struct sum3 { using number = typename sum<A,sum<B,C>>::number; };
As already mentioned, this is just a toy example. Anyhow, once you have a number type that can represent big enough numbers you just have to adjust you fib with minor modifications:
template<long long num> struct fib {
using result_t = typename sum3< typename fib<num-1>::result_t,
typename fib<num-2>::result_t,
typename fib<num-3>::result_t
>::number;
};
template<> struct fib<0> { using result_t = my_number<0,1>; };
template<> struct fib<1> { using result_t = my_number<0,1>; };
template<> struct fib<2> { using result_t = my_number<0,2>; };
template<> struct fib<3> { using result_t = my_number<0,4>; };
int main() {
fib<100>::result_t::print();
}
I couldn't find a reliable source for the correct value of fib<100>, so unfortunately I couldn't test against that.
Full example is here.
You can accomplish this using Boost version 1.72 and boost::multiprecision:
#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>
template <int x>
struct fib
{
static constexpr boost::multiprecision::uint1024_t value = x * fib<x - 1>::value;
};
template <>
struct fib<0>
{
static constexpr boost::multiprecision::uint1024_t value = 1;
};
int main()
{
std::cout << fib<100>::value;
}
Output:
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
This was run using Visual Studio 2019 and boost 1.72. Note that earlier versions of boost::multiprecision were not full constexpr, so this probably will not compile with earlier versions of boost.
EDIT:
Here is the third-class version. This is almost verbatim to the original poster's version, with the only difference being the usage of the constexpr-enabled big number class from boost:
#include <iostream>
#include <boost/multiprecision/cpp_int.hpp>
template<long long num>
struct fib
{
static constexpr boost::multiprecision::uint1024_t value = fib<num - 1>::value + fib<num - 2>::value + fib<num - 3>::value;
};
template<>
struct fib<0>
{
static constexpr boost::multiprecision::uint1024_t value = 1;
};
template<>
struct fib<1>
{
static constexpr boost::multiprecision::uint1024_t value = 1;
};
template<>
struct fib<2>
{
static constexpr boost::multiprecision::uint1024_t value = 2;
};
template<>
struct fib<3>
{
static constexpr boost::multiprecision::uint1024_t value = 4;
};
int main()
{
std::cout << fib<100>::value;
}
Output:
180396380815100901214157639

Non-type template parameters, constructor and deduction guide

I want to have a struct template, that is defined by particular values of its components passed to it during construction, such that different values would create different C++ data types, and thought non-type template parameters may be useful for that.
Something like this (just a simple example to show the issue, the real struct will be more complex):
enum ElementType
{
TYPE1,
TYPE2
};
template<ElementType elementType, int size>
struct DataType
{
DataType(ElementType et = elementType, int s = size):
elementType_(et),
size_(s)
{
}
ElementType elementType_;
int size_;
};
int main()
{
auto d1 = DataType(ElementType::TYPE1, 1);
}
I try to build this with g++-8 -std=c++17 and it gives me the following error:
./main.cpp:23:42: error: class template argument deduction failed:
auto d1 = DataType(ElementType::TYPE1, 1);
^
../main.cpp:23:42: error: no matching function for call to ‘DataType(ElementType, int)’
../main.cpp:12:2: note: candidate: ‘template<ElementType elementType, int size> DataType(ElementType, int)-> DataType<elementType, size>’
DataType(ElementType et = elementType, int s = size):
^~~~~~~~
../main.cpp:12:2: note: template argument deduction/substitution failed:
../main.cpp:23:42: note: couldn't deduce template parameter ‘elementType’
auto d1 = DataType(ElementType::TYPE1, 1);
^
../main.cpp:23:42: error: expression list treated as compound expression in functional cast [-fpermissive]
../main.cpp:23:42: warning: left operand of comma operator has no effect [-Wunused-value]
Note that I cannot use type template arguments, since the two types of the arguments are fixed (ElementType and int), but DataType(ElementType::TYPE1, 1) must be of different type than DataType(ElementType::TYPE1, 2) and DataType(ElementType::TYPE1, 1) must be different than DataType(ElementType::TYPE2, 1).
You can define your template like this:
template<ElementType elementType, int size>
struct DataType
{
const ElementType elementType_ = elementType;
const int size_ = size;
};
And create an instance of it like this:
auto d1 = DataType<ElementType::TYPE1, 1>();
Demo
To make use of deduction the value you pass to the constructor needs to be a constant expression. Unfortunately values passed as parameter loose their constexpr properties. To prevent this behavior you can pass the values wrapped in types e.g. using std::integral_constant.
Exemplary usage:
#include <type_traits>
enum ElementType
{
TYPE1,
TYPE2
};
template<ElementType elementType, int size>
struct DataType
{
DataType(std::integral_constant<ElementType, elementType>, std::integral_constant<int, size> ic):
elementType_(elementType),
size_(ic)
{
}
ElementType elementType_;
int size_;
};
int main()
{
auto d1 = DataType(std::integral_constant<ElementType, TYPE1>{}, std::integral_constant<int, 1>{});
}
[live demo]
To make it more convenient to use you could wrap around integral const with constexpr suffix operators:
#include <type_traits>
enum ElementType
{
TYPE1,
TYPE2
};
template <class... Ts>
constexpr int ival(Ts... Vs) {
char vals[sizeof...(Vs)] = {Vs...};
int result = 0;
for (int i = 0; i < sizeof...(Vs); i++) {
result *= 10;
result += vals[i] - '0';
}
return result;
}
template <class T, class... Ts>
constexpr ElementType etval(T V, Ts... Vs) {
if (V == '1')
return TYPE1;
if (V == '2')
return TYPE2;
}
template <char... Vs>
std::integral_constant<int, ival(Vs...)> operator""_i() {
return {};
}
template <char... Vs>
std::integral_constant<ElementType, etval(Vs...)> operator""_et() {
return {};
}
template<ElementType elementType, int size>
struct DataType
{
DataType(std::integral_constant<ElementType, elementType>, std::integral_constant<int, size> ic):
elementType_(elementType),
size_(ic)
{
}
ElementType elementType_;
int size_;
};
int main()
{
auto d1 = DataType(1_et, 1_i);
}
[live demo]

How to accumulate template parameter pack?

Suppose I want to enable writing this:
template <int a,int b> struct add_base{ static const int value = a+b;};
template<int...a> using add = accumulate<add_base,0,a...>;
template <int a,int b> struct mult_base{ static const int value = a*b;};
template<int...a> using mult = accumulate<mult_base,1,a...>;
template <int a,int b> struct sqsum_base{ static const int value = a+b*b;};
template<int...a> using sqsum = accumulate<sqsum_base,0,a...>;
static_assert( add<1,2,3>::value == 6 );
static_assert( mult<2,2,2>::value == 8 );
static_assert( sqsum<1,2,3>::value == 14 );
My accumulate looks like this:
template <template <int,int> class G,
int first, int second,
int...more>
struct accumulate {
static const int value = accumulate<G,G<first,second>::value,more...>::value;
};
template <template <int,int> class G,
int first, int second>
struct accumulate<G,first,second> {
static const int value = G<first,second>::value;
};
Now I wonder if accumulate can be condensed by expanding the recusion inline, something like:
template <template <int,int> class G,
int first,int second,
int...more>
struct accumulate {
static const int value = G< G<first,second>::value , more...>::value;
};
This is wrong and will result in
error: Wrong number of template arguments (3 should be 2)
Is it possible to unpack the parameters to instantiate G recursively in one line? If not, how to write accumulate without having to write a specialization?
If your compiler has support for C++17 then you may want to utilize fold expression:
template<int ... x_item> struct
accumulate
{
static inline constexpr int const s_value{(0 + ... + x_item)};
};
static_assert(6 == accumulate<1, 2, 3>::s_value);
online compiler
Example of parametrized operation:
template<typename x_Op, int ... x_items> struct
accumulate
{
static inline constexpr int const s_value{(x_Op{0} + ... + x_Op{x_items}).value};
};
struct
sq_sum
{
int value;
};
inline constexpr sq_sum
operator +(sq_sum left, sq_sum right)
{
return sq_sum{left.value + right.value * right.value};
}
static_assert(14 == accumulate<sq_sum, 1, 2, 3>::s_value);
online compiler

Initialise static const int with static function

I have a template class with some integers as arguments. One static const integer (call it Length) of this class needs to be calculated based on the arguments. The calculation does need a loop (as far as I know) so a simple expression won't help.
static int setLength()
{
int length = 1;
while (length <= someTemplateArgument)
{
length = length << 1;
}
return length;
}
The returned length should be used to init Length. Lengthis used as a fixed length of an array so I need it to be constant.
Is there a solution for this issue? I know that constexp could help but I can't use C11 or later.
Using metaprogramming. Implementation of C++11 enable_if taken from cppreference.com
#include <iostream>
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
template <int length, int arg, typename = void>
struct length_impl
{
static const int value = length_impl<(length << 1), arg>::value;
};
template <int length, int arg>
struct length_impl<length, arg, typename enable_if<(length > arg)>::type>
{
static const int value = length ;
};
template <int arg>
struct length_holder
{
static const int value = length_impl<1, arg>::value;
};
template<int n>
struct constexpr_checker
{
static const int value = n;
};
int main()
{
std::cout << constexpr_checker< length_holder<20>::value >::value;
}