0

I'm trying to create a class that uses the value of an array as a value/type template parameter. See below

template< std::array<bool, M> arr>
class gf {
...
};

I believe my rationale for doing this is sane. This class implements operator overloading for addition and multiplication. These operations are only well defined if the instances of the class were instantiated with the same value and size of arr.

An example usecase:

std::array<bool,3> p1 = {0,0,1};
std::array<bool,3> p2 = {0,1,1};

gf<p1> a;
gf<p1> b;
gf<p2> c;

auto out1 = a + b; // works as both instances use p1
auto out1 = a + c; // Compiler error as we're adding incompatible types

My current work around is passing arr to the constructor call and throwing an error if any incompatible types are combined. I hope there's a better way to do this, thanks!

EDIT: Also, any other design pattern that might accomplish the same goals would be welcomed. I'm not married to using templates specialization, but it's a tool with which I'm familiar!

ytgsyk4h
  • 25
  • 5

1 Answers1

1

Purely from a technical aspect, you can(1) pass the address of a constexpr array with static storage duration as a non-type template parameter:

#include <array>

template<const auto& arr>
struct gf {
    gf& operator+=(const gf& /* rhs */) {
        // ...
        return *this;
    }
};

template<const auto& arr>
auto operator+(gf<arr> lhs, const gf<arr>& rhs) {
    lhs += rhs;
    return lhs;
}

int main() {
    // As we want to use the address of the constexpr std::array
    // at compile time, it needs to have static storage duration.
    static constexpr std::array<bool, 3U> p1{{0, 0, 1}};
    static constexpr std::array<bool, 3U> p2{{0, 1, 1}};
    
    gf<p1> a;
    gf<p1> b;
    gf<p2> c;
    
    auto out1 = a + b;  // OK.
    //auto out2 = a + c;  // Error: incompatible types.
}

such that every instantiation of the gf class template with a unique array object will become a unique type (/specialization).

This relies on C++17 for auto as a template parameter; for a similar C++11 approach:

#include <array>

template<std::size_t M, const std::array<bool, M>& arr>
struct gf {
    gf& operator+=(const gf& /* rhs */) {
        // ...
        return *this;
    }
};

template<std::size_t M, const std::array<bool, M>& arr>
gf<M, arr> operator+(gf<M, arr> lhs, const gf<M, arr>& rhs) {
    lhs += rhs;
    return lhs;
}

int main() {
    // As we want to use the address of the constexpr std::array
    // at compile time, it needs to have static storage duration.
    static constexpr std::array<bool, 3U> p1{{0, 0, 1}};
    static constexpr std::array<bool, 3U> p2{{0, 1, 1}};
    
    gf<3U, p1> a;
    gf<3U, p1> b;
    gf<3U, p2> c;
    
    auto out1 = a + b;  // OK.
    //auto out2 = a + c;  // Error: incompatible types.
}

(1) This answer does not, in any way, try to present this as any kind of good approach for the XY-esque problem of the OP.

dfrib
  • 70,367
  • 12
  • 127
  • 192
  • Thank you for the detailed answer! I can see why this may not be a recommended approach. In your opinion, what would be a better design pattern for this type of problem? – ytgsyk4h Jul 20 '20 at 15:28
  • As I don't see anyone else coming to give additional feedback on my design pattern, I'll accept this answer as it addresses my concern on can/how to accomplish the behaviour. I'd still love to hear from anyone who has a better idea of how to accomplish my goal. – ytgsyk4h Jul 21 '20 at 18:06
  • @ytgsyk4h I missed you previous comment: short of using literal classes as non-type template parameters in C++20, I can't come up with anything at the top of my head. – dfrib Jul 21 '20 at 18:12