如何有条件地将函数添加到类模板中
How to conditionally add a function to a class template?
我有一个矩阵类模板,如下所示:
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];
}
};
我想要的是定义一个.setIdentity()
函数,仅用于编译时nrows==ncols
为true
时的实例化。并且当nrows==ncols
是false
时,将有没有对.setIdentity()
的定义。
我正在尝试使用enable_if
习惯用法,但这将定义所有情况下的函数。不是吗?
您可以在以下模式中使用std::enable_if
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
的完整示例
#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;
}
---编辑---
正如Niall在评论中所指出的(关于TemplateRex的答案,但我的解决方案也有同样的缺陷(,这个解决方案可以通过这种方式循环转换,明确行和列的数量
mi3.setIdentity<4, 4>();
(但这不是一个真正的问题(IMHO(,因为mi3
是一个正方形矩阵,setIdentity()
可以与真实维度(nrows
和ncols
(一起工作(,甚至与一起工作
mnoi.setIdentity<4, 4>()
(这是一个大问题(IMHO(,因为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 */
}
或者类似的东西(,但是我建议在CCD_ 15中添加相同的检查。
我是说
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 */ }
懒惰和不必要的重复方式
只需添加一个部分专业化:
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!"; }
};
实时示例
对于一般的N * M
矩阵,一般模板将被实例化,而仅对于N * N
矩阵,这种专门化是更好的匹配。
缺点:所有正则代码的代码重复。可以使用基类,但实际上做一些SFINAE魔术更容易(如下(
一种稍微困难但更经济的方式
您还可以通过添加隐藏模板参数N
和M
来使用SFINAE,这些参数默认为nrows
,ncols
到setidentity
,并在条件为N == M
时添加到enable_if
。
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!";
}
};
或者,由于问题标记为C++11,请改用typename std::enable_if<(N == M)>::type
。
实时示例
使用伪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>>
{
// ...
在这里,如果我们不是对角的,我们从零继承,如果我们是对角的,则实现集合标识的类。
如果正确的话,Matrix
的用户会神奇地从其父级获得.setIdentity()
。
CCD_ 29内部的CCD_。
这是伪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;
一切都应该编译。
一个基本但简单的解决方案,其他答案都没有提到:您可以使用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();
}
如果需要所有子对象共享数据,仍然可以在层次中向下移动数据
这主要取决于真正的问题。
skypjack和max66都给出了这个问题的简单答案。这只是另一种方法,使用简单的继承,尽管这意味着使用一个子类来处理平方矩阵:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
protected:
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
template<typename T, std::size_t N>
class SqMatrix : public Matrix <T, N, N>
{
public:
setIdentity()
{
//Do whatever
}
}
- 哈希表添加函数
- 汇编错误C 在标题文件中添加函数时
- 为什么添加函数在C 11线程中无效
- 在类的快捷菜单上找不到添加函数
- 单链表添加函数-读取访问冲突
- 无法向 MFC ActiveX 添加函数
- 在运行时向对象添加函数集合
- C++ - 如何添加函数以处理货币计算器的多种情况
- 添加函数数组并随机选择其中一个
- 我正在尝试为 c++ 中的数组创建一个添加函数
- Qt从其他线程向事件循环添加函数调用
- 添加参数或添加函数
- 向typedef添加函数
- 在双链表中添加函数会导致无限循环
- 添加函数erf()和erfc()到math.h . _ C基础
- 重载<<运算符和添加函数的问题
- c++初学者.添加函数并调用它失败
- 如果在向类中添加函数时取消选中内联框,则在classname.h中只需要函数原型
- 我终于在链表中制作了我的添加函数,但不能在main中使用.:(.
- 在Qt中在c++中添加C函数时的构建问题