C++ 如何有条件地将函数添加到类模板中?
我有一个矩阵类模板,如下所示:C++ 如何有条件地将函数添加到类模板中?,c++,class,templates,c++11,template-specialization,C++,Class,Templates,C++11,Template Specialization,我有一个矩阵类模板,如下所示: 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]; } }; 模板 类矩阵 { T数据[nrows][ncol
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];
}
};
模板
类矩阵
{
T数据[nrows][ncols];
公众:
运算符()(std::size\u T i,std::size\u T j)
{
返回数据[i][j];
}
};
我想要的是定义一个.setIdentity()
函数,仅用于编译时nrows==ncols
为true
时的实例化。当nrows==ncols
为false
时,将no定义.setIdentity()
我尝试的是使用enable_if
idiom,但这将定义所有情况下的函数。不是吗?懒惰和不必要的重复方式
只需添加部分专门化:
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!"; }
};
或者,因为问题被标记为C++11,所以改用typename std::enable\u if::type
您可以在以下模式下使用
std::enable_
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
(这是一个大问题(IMHO),因为mnoi
不是平方矩阵)
显然,尼尔提出了一个解决方案(添加一个静态断言
;类似于
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 */
}
使用伪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>>
{
// ...
所有的东西都应该编译。这是一个基本但简单的解决方案,任何其他答案都没有提到:您可以使用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();
}
#包括
#包括
结构HasSetIdentity{
void setIdentity(){}
};
结构HasNotSetIdentity{};
模板
类矩阵:public std::conditional::type
{
T数据[nrows][ncols];
公众:
运算符()(std::size\u T i,std::size\u T j)
{
返回数据[i][j];
}
};
int main(){
矩阵m1;
m1.setIdentity();
矩阵m2;
//方法不可用
//m2.setIdentity();
}
如果需要所有子对象共享数据,您仍然可以在层次结构中向下移动数据。
这主要取决于实际问题。两人都给出了问题的简单答案。这只是一种替代方法,使用简单继承,尽管这意味着对平方矩阵使用子类:
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
}
}
模板
类矩阵
{
受保护的:
T数据[nrows][ncols];
公众:
运算符()(std::size\u T i,std::size\u T j)
{
返回数据[i][j];
}
};
模板
类SqMatrix:公共矩阵
{
公众:
setIdentity()
{
//做任何事
}
}
也许你不用有条件地改变现有对象,只需要一个静态函数,比如make_identity
,它返回一个矩阵
@GManNickG:目前为止最喜欢的解决方案(因为它是最简单的!)请考虑将它作为一个答案添加进来。这个解决方案似乎重复了所有非特殊情况已经完成的工作。它真的值得重复吗?这需要两次实现整个类。@ TeMeleReX:你的第二代码似乎不编译,甚至<代码> M22。
行。查看我的链接。我需要它与C++11
兼容,似乎您使用了GCC 6.1.0版本。@TemplateRex:实际上,即使是与较新的C++11解决方案链接一起使用。查看链接,它给出了错误:在'struct std::enable_if'
中没有名为'type'的类型。我可能会添加一个静态_断言(N==nrows&&M==ncols,“无效”)
到setidentity
以避免愚蠢的客户端行为,如;Matrix sq;sq.setidentity();
可爱的想法,但是如果有多个模块要执行,则需要标记空\u t
support@template也许
就是为了让它无缝而写的。@Yakk:这个用法会被称为mixin吗?(我觉得是这样的…)@matt不知道模式的名称。抱歉。即使是我一直使用的模式,我也经常弄错名称。@Yakk:非常感谢。@skypkack:非常感谢!我认为这是这里给出的所有方法中最干净、最惯用的方法。应该防止mi3.setidentity()
@TemplateRex-感谢您报告此问题;答案已修改。是的,如果来自叮当声的错误信息丰富,+1规避问题可以通过在模板参数之前添加类型名…
来解决。模板参数包在正常情况下将被推断为空,而美国er不能再显式地提供其他参数了。@L.F.-Wow!你的解决方案非常优雅。而且有效。但我不明白它到底是如何工作的。我的意思是……我看到了编写mnoi.setIdentity();
,或者mnoi.setIdentity();
,给出了一个编译错误。但是,如果参数是不同的,为什么不能在变量包之后解释参数呢?我的意思是……你能说标准的哪一部分禁止这样做吗?
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 */
}
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<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>>
{
// ...
template<bool b, class T>
using maybe=typename std::conditional<b, T, empty_t<T>>::type;
#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();
}
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
}
}