Reputation: 114
I'm not sure "data polymorphism" is a actually a thing, but it seemed like a reasonable expression for what I'm looking for: How would you represent multiple types that share a functionality, but operate on different static data?
Take for instance:
class A {
static constexpr std::array<int, 5> m_static_data{1, 2, 3, 4, 5};
public:
int foo(float x) { // do stuff based on m_static_data }
};
class B {
static constexpr std::array<int, 3> m_static_data{42, 43, 44};
public:
int foo(float x) { // do stuff based on m_static_data }
};
Where foo()
is exactly the same between them.
One way would be to define foo
in a base class, and a getter for the data in A and B:
class Base {
virtual std::vector<int> get_data() = 0;
public:
int foo(float x) { // do stuff based on get_data()'s returned value }
};
class A : Base{
std::vector<int> get_data() override { return {1, 2, 3, 4, 5}; }
};
class B : Base{
std::vector<int> get_data() override {return {42, 43, 44}; }
};
But then you would lose all the nice compile time checks and evaluations. Is there a better pattern for doing this sort of thing? And is there a name for it?
Upvotes: 1
Views: 154
Reputation: 51915
How would you represent multiple types that share a functionality …
This is really an archetypal definition of what a 'simple' class template is for; virtually all containers provided by the STL do just this: a std::vector<char>
and std::vector<double>
have a whole variety of member functions (and associated, free-standing functions) that do the same thing but on different data types.
… but operate on different static data?
Declaring static
data members of templated classes is a bit trickier, as is the use of constexpr
members, but here's a short example of how you might go about doing so:
template<typename T>
class Base {
static const T m_static_data;
public:
T foo(float x) {
return static_cast<T>(x * static_cast<float>(m_static_data));
}
};
// Instantiate static data for "int" and "double" types ...
template<>
inline constexpr double Base<double>::m_static_data = 3.1415926536;
template<>
inline constexpr int Base<int>::m_static_data = 21;
int main()
{
// Use "A" for a double and "B" for an int class object...
Base<double> A;
Base<int> B;
std::cout << A.foo(1.0f) << std::endl;
std::cout << B.foo(2.0f) << std::endl;
return 0;
}
Note that the use of the inline
keyword on the definitions requires a compiler conforming to at least C++17.
Upvotes: 1
Reputation: 32293
You can try using static polymorphism (CRTP idiom). It will allow you to use common functionality at compile time.
#include <iostream>
#include <memory>
#include <array>
template<typename Derived>
class Base {
public:
void foo() {
const auto& arr = static_cast<Derived*>(this)->m_static_data;
std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
};
class A : public Base<A> {
friend class Base<A>;
static constexpr std::array<int, 5> m_static_data{1, 2, 3, 4, 5};
};
class B: public Base<B> {
friend class Base<B>;
static constexpr std::array<int, 3> m_static_data{42, 43, 44};
};
int main()
{
auto a = A();
auto b = B();
a.foo();
b.foo();
return 0;
}
Upvotes: 1