11 c++ templates class template-specialization c++11
我有一个Matrix类模板如下:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
Run Code Online (Sandbox Code Playgroud)
我要的是定义一个.setIdentity()当实例的唯一功能nrows==ncols是true在编译的时候.而且会有任何的定义.setIdentity()时,nrows==ncols是false.
我正在尝试使用的是enable_if成语,但这将为所有情况定义函数.不是吗?
只需添加部分专业化:
template<typename T, std::size_t N>
class Matrix<T, N, N>
{
T data[N][N];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
void setidentity(/*whatever params*/) { std::cout << "yay!"; }
};
Run Code Online (Sandbox Code Playgroud)
对于一般N * M矩阵,通用模板将被实例化,而仅对于N * Nmatrics,此专门化是更好的匹配.
缺点:代码重复所有常规代码.可以使用基类,但实际上更容易做一些SFINAE魔术(下面)
您也可以通过添加隐藏的模板参数使用SFINAE N和M那个默认nrows和ncols到setidentity,并enable_if在状态N == M.
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
template <std::size_t N = nrows, std::size_t M = ncols, std::enable_if_t<(N == M)>* = nullptr>
void setidentity(/*whatever params*/) {
static_assert(N == nrows && M == ncols, "invalid");
std::cout << "yay!";
}
};
Run Code Online (Sandbox Code Playgroud)
或者,由于问题标记为C++ 11,请typename std::enable_if<(N == M)>::type改用.
您可以std::enable_if在以下模式下执行此操作
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
Run Code Online (Sandbox Code Playgroud)
一个完整的例子
#include <type_traits>
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{ return data[i][j]; }
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
};
int main()
{
Matrix<int, 3, 3> mi3;
Matrix<int, 3, 2> mnoi;
mi3.setIdentity();
// mnoi.setIdentity(); error
return 0;
}
Run Code Online (Sandbox Code Playgroud)
---编辑---
正如Niall的评论(关于TemplateRex的答案,但我的解决方案遭受同样的缺陷)所指出的那样,这个解决方案可以通过这种方式表示行和列的数量
mi3.setIdentity<4, 4>();
Run Code Online (Sandbox Code Playgroud)
(但这不是一个真正的问题(恕我直言),因为它mi3是一个方形矩阵,setIdentity()可以使用真实尺寸(nrows和ncols))或甚至与
mnoi.setIdentity<4, 4>()
Run Code Online (Sandbox Code Playgroud)
(这是一个大问题(恕我直言),因为mnoi它不是方阵).
显然,Niall提出了解决方案(添加一个static_assert类似的东西)
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{
static_assert(r == nrows && c == ncols, "no square matrix");
/* do something else */
}
Run Code Online (Sandbox Code Playgroud)
或类似的东西)但我建议添加相同的检查std::enable_if.
我的意思是
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if< (r == c)
&& (r == nrows)
&& (c == ncols)>::type setIdentity ()
{ /* do something */ }
Run Code Online (Sandbox Code Playgroud)
任何其他答案都没有提到的基本但简单的解决方案:您可以使用std::conditional和继承。
它遵循一个最小的工作示例:
#include<type_traits>
#include<cstddef>
struct HasSetIdentity {
void setIdentity() { }
};
struct HasNotSetIdentity {};
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public std::conditional<(nrows==ncols), HasSetIdentity, HasNotSetIdentity>::type
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
int main() {
Matrix<int, 2,2> m1;
m1.setIdentity();
Matrix<int, 2,3> m2;
// Method not available
// m2.setIdentity();
}
Run Code Online (Sandbox Code Playgroud)
如果您需要所有子对象共享数据,您仍然可以将数据沿层次结构向下移动。
这主要取决于实际问题。
使用伪 CRTP 为某些内容添加模块化支持。
template<class T, std::size_t nrows, std::size_t ncols>
class Matrix;
template<class T, std::size_t size>
struct MatrixDiagonalSupport {
auto self() { return static_cast<Matrix<T, size, size>*>(this); }
auto self() const { return static_cast<Matrix<T, size, size> const*>(this); }
void setIdentity() {
for (std::size_t i = 0; i < size; ++i) {
for (std::size_t j = 0; j < i; ++j) {
(*self())(i,j) = {};
}
(*self())(i,i) = 1; // hope T supports this!
for (std::size_t j = i+1; j < size; ++j) {
(*self())(i,j) = {};
}
}
}
};
template<class T>
struct empty_t {};
template<bool b, class T>
using maybe= std::conditional_t<b, T, empty_t<T>>;
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public maybe<nrows==ncols,MatrixDiagonalSupport<T, nrows>>
{
// ...
Run Code Online (Sandbox Code Playgroud)
在这里,如果我们不是对角线,我们从无继承,如果我们是对角线,我们将继承一个实现集合标识的类。
如果正确,用户Matrix会.setIdentity()神奇地从其父级获取。
static_castinsideself()最终成为一个零成本的抽象,并允许基类访问子类。
这是伪 CRTP,因为我们实际上并没有将派生类类型传递给父类,只是父类重建它的足够信息。
该解决方案使该方法成为一种实际方法,并避免了任何类型的 SFINAE 诡计。
在 C++11 中替换conditional_t<?>为typename conditional<?>::type:
template<bool b, class T>
using maybe=typename std::conditional<b, T, empty_t<T>>::type;
Run Code Online (Sandbox Code Playgroud)
一切都应该编译。
| 归档时间: |
|
| 查看次数: |
2588 次 |
| 最近记录: |