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
    }
}