I'm trying to write a certain class with the following functionality.
Outside, I have a collection of classes which define a type and a member function:
class X
{
public:
using energy_t = std::array<double,N>; // N is some number 1,2,3...
energy_t calc_energy(/*params*/);
// everything else
};
I want to create a class template that can contain different combinations of such small classes and invoke some functionality on them.
The requirement is that all energy_t
s in such collections are the same.
Here is the first approach:
template<typename Part, typename... Parts>
requires (std::is_same_v<typename Part::energy_t, typename Parts::energy_t> && ...)
class State
{
public:
static constexpr int degeneracy = std::tuple_size_v<typename Part::energy_t>;
// right now is deduced from given types
using energy_t = std::array<double, degeneracy>;
private:
std::tuple<Part, Parts...> _parts;
public:
State(Part&& part, Parts&&... parts) : _parts(part, parts...) {}
// functionality, usually with std::apply() on _parts tuple member
}
This serves almost all needs, and is nicely instantiated with CTAD (which is important, because some arguments are not default-constructible):
State state{X(), Y(/*ctor-params*/), Z()};
Problem: At the moment, the only thing checked is that all energy_t = std::array<double,N>
s are the same. However, it is not specified, what number N
is desired. This would be the 'perfect' syntax for the construction:
State<3> state{X(), Y(/*params*/), Z()};
// only types with energy_t = std::array<double,3> are accepted
because it is just a clear statement: State<3>
is a three-fold degenerate state with {X(), Y(), ...}
as its parts, that are constructed in-place.
However, it seems impossible, because it's forbidden to specify only a part of template parameters.
One thing that comes to mind is just adding another template parameter and the same to the constructor and proceeding with CTAD, like
template<size_t N, typename Part, typename Parts...>
class State
{
State(size_t N, Part&& part, Parts&&... parts) : _parts(part, parts) {};
// etc
};
State state{3, X(), Y()};
Is there another way to solve this?