0

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_ts 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?

cigien
  • 57,834
  • 11
  • 73
  • 112
Bbllaaddee
  • 145
  • 1
  • 9
  • 3
    Define a free function like `template auto make(...)` and do the check in there. – user2407038 Oct 28 '21 at 23:18
  • Since you want to be able to write `State<3>`, why is the first template parameter not a `size_t` for which `3` could be specified? – JaMiT Oct 28 '21 at 23:32
  • @JaMiT, yeah, that's pretty much a solution I give in the end: add one template parameter + one constructor parameter. Gonna state it more clearly) – Bbllaaddee Oct 28 '21 at 23:50
  • 1
    @Bbllaaddee Since you have a solution, why do you ask for other possibilities? Is there something you find lacking in your current solution? – JaMiT Oct 29 '21 at 00:46
  • 1
    As an alternative to `auto state = make_state<3>(...);` you could also get `Fold<3>::State state(...);` to work. – super Oct 29 '21 at 01:01
  • @user2407038, nice one! Why can you specify only a part of function template parameters, but that's not the case for class template? – Bbllaaddee Oct 29 '21 at 01:31
  • @super, could you please say, what exactly is a Fold<> here? – Bbllaaddee Oct 29 '21 at 01:31
  • It's a class, and State would be declared inside it. – super Oct 29 '21 at 02:05
  • `State state{3, X(), Y()};` might only do check at runtime, you need something like `State state{std::integral_constant{}, X(), Y()};` to have compile time check. – Jarod42 Oct 29 '21 at 08:30

0 Answers0