继承vs类模板专门化.设计的家伙
Inheritance vs class template specialization. Design dude
我在实现设计上遇到了麻烦。我希望你能帮助我。假设我有以下类
class A
{
public:
vector<int> v() const { return m_v; }
bool isValid() const { return m_v.size() > m_components; }
int operator [] (const int index) const { return m_v[index]; }
...
private:
vector<int> m_v;
int m_components;
}
现在我想让m_v
向量可以是不同的类型,所以我可以模板类:
template<typename T>
class A
{
public:
vector<T> v() const { return m_v; }
T operator [] (const int index) const { return m_v[index]; }
...
private:
vector<T> m_v;
int m_components;
}
然而,我意识到当类型T
是例如double
时,我需要扩展A
类并添加更多属性,例如另一个vector<bool> m_foo;
,并更改几个应该使用这些新属性的方法。
这就是我怀疑的地方。我想我有几个选择:
选项1:我可以用所有常用方法的实现创建一个非模板化的基类A
,并派生几个类,每个类对应不同的类型(具有自己特定的类属性和方法实现),即:Aint, Adouble, Afloat
。此选项要求将vector<...> m_v;
存储在每个派生类中,因此我必须多次复制所有相同的代码以访问每个派生类中的m_v;
属性。在示例中,这样的方法只有v()
, operator []
和isValid()
,然而在实际问题中,有更多的方法。
选项2:模板特化。我可以为每种类型专门化类模板,因此只提供根据T
类型而变化的特定方法的实现。然而,这强制在模板类中存储大量只在T
是特定类型时使用的东西,即m_foo
向量只在T
类型是double
时使用(在建议的示例中)。因此,我是在浪费记忆。此外,实现一个模板类并为几乎大多数模板类型提供模板类专门化,并存储仅用于特定类型的特定属性,似乎不是很优雅,甚至不一致。
提前谢谢你。哈维尔。
这得看情况。
一般的经验法则是问自己"double是不是A"。当它们有is-a关系时,应该使用继承。
但是你也有类型依赖,这不是一个真正的"is-a"关系。
你也可以使用这两个选项:拥有一个具有通用功能的基类,它接受一个模板参数,并拥有具有它们需要的附加功能的子类。所以你不需要重新实现所有依赖于类型的函数
:
template<typename T>
class A
{
public:
vector<T> v() const { return m_v; }
T operator [] (const int index) const { return m_v[index]; }
...
private:
vector<T> m_v;
...
};
class ADouble : public A<double>
{
...
};
顺便说一句:为什么你认为模板占用更多的内存?
用子操作部分专门化类中的特定操作(不专门化整个类)。
#include <vector>
namespace detail
{
// general concept of indexing into something
template<class T> struct index_operation;
// indexing into most vectors
template <class T> struct index_operation<std::vector<T>>
{
T& operator()(std::vector<T>& v, std::size_t i) const
{
return v[i];
}
T const& operator()(std::vector<T> const& v, std::size_t i) const
{
return v[i];
}
};
// indexing into a vector<bool>
template <> struct index_operation<std::vector<bool>>
{
std::vector<bool>::reference operator()(std::vector<bool>& v, std::size_t i) const
{
return v[i];
}
std::vector<bool>::const_reference operator()(std::vector<bool> const& v, std::size_t i) const
{
return v[i];
}
};
}
template<typename T>
class A
{
using vector_type = std::vector<T>;
public:
std::vector<T> v() const { return m_v; }
decltype(auto) operator [] (const int index) const
{
auto op = detail::index_operation<vector_type>();
return op(m_v, index);
}
private:
std::vector<T> m_v;
};
我将尝试用一个与我的实际问题密切相关的例子来阐述这个问题,尽管这将成为一个较长的帖子。
Option1:考虑以下基于模板和模板专门化的类实现。
template<typename T>
class A
{
public:
A() {}
vector<T> v() const { return m_v; }
bool isValid() const { return m_v.size() >= m_components; }
T operator [] (const int i) const { return m_v[i]; }
T& operator [] (const int i) { return m_v[i]; }
int components() const { return m_components; }
double value() const { return m_value; }
void method1();
private:
vector<T> m_v;
int m_components;
double m_value;
vector<bool> m_indices; // this is only used when T is int
map<int, char> m_map; // this is only used when T is double
queue<int> m_queue; // this is only used when T is bool
};
template<>
void A<int>::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
// stuff only for int case
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_indices[i] = true : m_indices[i] = false; }
}
template<>
void A<double>::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
// stuff only for double case
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_map[i] = 'e' : m_map[i] = 'o'; }
}
template<>
void A<bool>::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
// stuff only for bool case
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_queue.push(1) : m_queue.push(0); }
}
可以看到,无论T
类型是什么,都有几个方法是通用的,并且涉及到m_v
向量的使用(方法:v()
, isValid()
, operator[]
)。然而,也有其他方法(method1()
)根据T
类型有特定的实现,并且也需要根据该类型使用特定的数据结构(queues
, maps
, vectors
)。我看到非常非常丑陋的队列,映射,向量等在类的定义,虽然如果他们只在具体情况下使用取决于T
类型。
选项2:另一种选择:
class A
{
public:
A() {}
int components() const { return m_components; }
double value() const { return m_value; }
virtual void method1() == 0;
protected:
int m_components;
double m_value;
};
/***** Derived A for int case ****/
class Aint : public A
{
public:
Aint() {}
vector<int> v() const { return m_v; }
bool isValid() const { return m_v.size() >= m_components; }
int operator [] (const int i) const { return m_v[i]; }
int& operator [] (const int i) { return m_v[i]; }
void method1();
private:
vector<int> m_v;
vector<bool> m_indices;
};
void Aint::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_indices[i] = true : m_indices[i] = false; }
}
/***** Derived A for double case ****/
class Adouble : public A
{
public:
Adouble() {}
vector<double> v() const { return m_v; }
bool isValid() const { return m_v.size() >= m_components; }
double operator [] (const int i) const { return m_v[i]; }
double& operator [] (const int i) { return m_v[i]; }
void method1();
private:
vector<double> m_v;
map<int, char> m_map;
};
void Adouble::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_map[i] = 'e' : m_map[i] = 'o'; }
}
/***** Derived A for bool case ****/
class Abool : public A
{
public:
Abool() {}
vector<bool> v() const { return m_v; }
bool isValid() const { return m_v.size() >= m_components; }
bool operator [] (const int i) const { return m_v[i]; }
bool& operator [] (const int i) { return m_v[i]; }
void method1();
private:
vector<bool> m_v;
queue<int> m_map;
};
void Abool::method1()
{
for (int i = 0; i < m_components; ++i) m_v.push_back(i);
for (int i = 0; i < m_components; ++i) { i % 2 == 0 ? m_queue.push(1) : m_queue.push(0); }
}
如您所见,在这种情况下,特定类型的数据结构(queues
、maps
等)仅为它们所需的情况定义(不在选项1的通用类模板中)。但是,由于m_v
向量的特定类型,现在应该在每个派生类中定义它。因此,访问和操作vector的东西应该总是在所有派生类中复制,尽管它们总是相同的(方法v()
, isValid()
, operator[]
等)。它似乎也没有设计好。
这个目的的最佳设计是什么?谢谢你
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 如何为模板化对象创建模板向量?VS正在投掷C3203
- 数据成员SFINAE的C++17测试:gcc vs clang
- 为什么在Windows上的VS 2019和Clang 9中"size_t"在没有标题的情况下工作
- 在for循环中使用auto vs decltype(vec.size())来处理字符串的向量
- 是否可以对零模板参数进行模板专门化
- 正在VS调试器中监视映射条目
- Confusion: decltype vs std::function
- 将IBM Rhapsody模型集成到VS 2019中
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- 使用VS Code和CMake Tools运行自定义命令
- 修改 VS Code 中的默认C++代码段
- 如何使用c++在VS 2019上运行SQL查询
- vs 2015 constexpr变量不恒定,但与2019相比还好吗
- 尝试根据类中 typedef 的存在来专门化模板函数
- 完美前进使用 std::forward vs RefRefCast
- 从VS 2015更新3更新到VS2015更新3 d后浮点计算行为不同的原因
- 下面的模板专门化代码是非标准的,或者是vs - c++中的错误
- 继承vs类模板专门化.设计的家伙
- 无法在 VS .NET 2008 中使用 boost::enable_if 专门化成员函数模板