Incluso si no terminas usándolos ahora, los mixins y los argumentos de la plantilla de política son elementos muy útiles para comprender. En este caso, son muy similares. Primero, una matriz con una base mixin. He usado C++ 0x mutex en lugar de openmp, pero debería tener la idea.
#include <iostream>
#include <vector>
#include <mutex>
template <class value_t, class base_t>
class array_t : private base_t {
std::vector<value_t> v_;
public:
array_t(size_t sz = 0) : v_ (sz) { }
value_t get(size_t i) const
{
this->before_get();
value_t const result = v_[i];
this->after_get();
return result;
}
void set(size_t i, value_t const& x)
{
this->before_set();
v_[i] = x;
this->after_set();
}
};
class no_op_base_t {
protected:
void before_get() const { }
void after_get() const { }
void before_set() const { }
void after_set() const { }
};
class lock_base_t {
mutable std::mutex m_;
protected:
void before_get() const { std::cout << "lock\n"; m_.lock(); }
void after_get() const { std::cout << "unlock\n"; m_.unlock(); }
void before_set() const { std::cout << "lock\n"; m_.lock(); }
void after_set() const { std::cout << "unlock\n"; m_.unlock(); }
};
int main()
{
array_t<double, no_op_base_t> a (1);
array_t<double, lock_base_t> b (1);
std::cout << "setting a\n";
a.set(0, 1.0);
std::cout << "setting b\n";
b.set(0, 1.0);
std::cout << "getting a\n";
a.get(0);
std::cout << "getting b\n";
b.get(0);
return 0;
}
Ahora la misma clase pero utilizando un enfoque de argumento de política en lugar de herencia.
#include <iostream>
#include <vector>
#include <mutex>
template <class value_t, class policy_t>
class array_t {
policy_t policy_;
std::vector<value_t> v_;
public:
array_t(size_t sz = 0) : v_ (sz) { }
value_t get(size_t i) const
{
policy_.before_get();
value_t const result = v_[i];
policy_.after_get();
return result;
}
void set(size_t i, value_t const& x)
{
policy_.before_set();
v_[i] = x;
policy_.after_set();
}
};
class no_op_base_t {
public:
void before_get() const { }
void after_get() const { }
void before_set() const { }
void after_set() const { }
};
class lock_base_t {
mutable std::mutex m_;
public:
void before_get() const { std::cout << "lock\n"; m_.lock(); }
void after_get() const { std::cout << "unlock\n"; m_.unlock(); }
void before_set() const { std::cout << "lock\n"; m_.lock(); }
void after_set() const { std::cout << "unlock\n"; m_.unlock(); }
};
int main()
{
array_t<double, no_op_base_t> a (1);
array_t<double, lock_base_t> b (1);
std::cout << "setting a\n";
a.set(0, 1.0);
std::cout << "setting b\n";
b.set(0, 1.0);
std::cout << "getting a\n";
a.get(0);
std::cout << "getting b\n";
b.get(0);
return 0;
}
En este caso, ambos son muy similares. La diferencia importante es que la mezcla podría definir algunos métodos para ser virtual y le permitirá cambiar el comportamiento de la matriz heredando de ella. Como en el siguiente:
template <class value_t>
class mk_virtual_base_t {
protected:
void before_get() const { }
void after_get() const { }
void before_set() const { }
void after_set() const { }
virtual value_t get(size_t) const = 0;
virtual void set(size_t, value_t) = 0;
};
template <class value_t>
class daily_wtf_contender_t : public array_t<value_t, mk_virtual_base_t<value_t> > {
virtual value_t get(size_t) const { std::cout << "surprise! get is virtual!\n"; return 0; }
virtual void set(size_t, value_t) { std::cout << "surprise! set is virtual!\n"; }
};
Si bien hay algunos casos reales donde la ventaja mixin es útil no es tan a menudo. Por eso, cuando se trabaja con plantillas, el enfoque de política suele ser el más apropiado. Los argumentos de política son utilizados por la biblioteca estándar en muchos lugares, por lo que hay algunos buenos ejemplos para estudiar.
En cuanto a su pregunta sobre "heredar interfaz, no implementación". Usado hereda cuidadosamente la implementación es bastante útil. Lo mismo ocurre con la herencia múltiple. Solo necesita ser juicioso acerca de cuándo los usa.
Me parece que agregar un parámetro de política a su plantilla 'MyArray' probablemente funcionaría mejor. –