I\'ve a class that must depend for some reasons from an int
template parameter.
For the same reasons, that parameter cannot be part of the parameter list fo
I think that solution with "traits" is the best for most cases.
Only to make a little more "mess" in this issue I will provide two more alternatives. Maybe in some very specific cases - they can be in some way better.
class C
only differs in its constructor from your original code:
class C final {
// All B and D defined as in OP code
public:
// Here the change - c-tor just accepts D<int>
template <int size>
explicit C(D<size>* b) : b(b) {}
// all the rest as in OP code
};
The prototype - template global variable:
template <int N>
C c{new C::D<N>()};
// this variable should be rather const - but foo() is not const
// and copy semantic is not implemented...
And usage:
int main() {
// you did not implement copy semantic properly - so just reference taken
C& c = ::c<3>;
c.foo();
}
int
This solution, although looks pretty promising I would personally avoid - that only complicates design - and some possibility of object slicing is here present too.
class CBase {
// all here as in OP code for C class
public:
// only difference is this constructor:
template<int size>
explicit CBase(D<size>* b) : b(b) {}
};
Then - the final class:
template <int N>
class C final : private CBase {
public:
C() : CBase(new CBase::D<N>()) {}
using CBase::foo;
};
The usage:
int main() {
C<3> c;
c.foo();
}
Q: One can ask in which way solution with Base class is better than just adding int
as another parameter.
A: by base implementation class you do not need to have many "copies" of the same code - you avoid template code bloat...
You have to pass in something that can be deduced. The simplest thing to use is just a empty wrapper for an int: std::integral_constant. Since you only want int
s I believe, we can alias it and then only accept that specific type:
template <int N>
using int_ = std::integral_constant<int, N>;
Where your C
constructor just accepts that:
template <int N>
explicit C(int_<N> ) {
b = new D<N>{};
}
C c{int_<3>{}};
You could even go all out and create a user-defined literal for this (a la Boost.Hana) so that you can write:
auto c = 3_c; // does the above
Also, consider simply forwarding the trait through to D
. Metaprogramming works better if everything everywhere is a type. That is, still accept the same int_
in C
:
template <class T>
explicit C(T ) {
b = new D<T>{};
}
Where now D
expects something that has a ::value
:
template <class T>
struct D: public B{
static constexpr int N = T::value;
void foo() {
using namespace std;
cout << N << endl;
}
std::array<int, N> arr;
};
It's the same thing either way from the user of C
's perspective, but just worth a thought.
use template specialization and inheritance:
#include <iostream>
using namespace std;
template <int num> struct A {
A() { cout << "generic" << endl; }
};
template <> struct A<1> {
A() { cout << "implementation of 1" << endl; }
};
template <int num>
struct B : public A<num> {
B() : A<num>() {}
};
int main(int argc, char *argv[])
{
B<1> b;
B<3> b1;
B<4> b2;
}
edit: or you can do it even easier:
template <int num>
struct A {
A();
};
template <int num>
A<num>::A() { cout << "general " << num << endl; }
template <>
A<1>::A() { cout << "specialization for 1" << endl; }