1

我正在编写代码并决定添加对标量类型的支持,以便能够在std::complex<double>和 plain之间切换double。我把所有的数字运算都写好了,并用一种template <typename Scalar>​​方法对其进行了模板化。

我的“问题”是,对于复杂的情况,我需要提供一些在实际情况下没有意义且无法编码的附加方法。

我以前的方法是创建一个基类模板,它实现了所有常见的东西。然后我有一个派生自这个基类的具体类,作为Scalar模板参数std::complex<double>。然后我有一种模板代理/虚拟类,它充当double版本和std::complex<double>. 它看起来或多或少像下面。

//base stuff
template<typename Scalar>
class NumberCruncherBase{
 Scalar stuff1();
 Scalar stuff2();
 Scalar stuff3();
}

//inherit base stuff and extend it
class NumberCruncherComplex : public NumberCruncherBase< std::complex<double> >{
 std::complex<double> extra_stuff1();
}

//switch proxy
template<typename Scalar>
class NumberCruncher : public NumberCruncherBase<Scalar> {}

//specialization for complex to explicitly derive from the extension
//in case of complex
template<>
class NumberCruncher< std::complex<double> > : public NumberCruncherBase< std::complex<double> > {}

令人惊讶与否,这种方法效果不错。NumberCruncher您可以从具体的专门类型派生或直接派生。也可以提供NumberCRuncherReal一致性,但这有点毫无意义。

然而,这对代码来说有点麻烦,而且重复的代码感觉很臃肿。我需要为 Base 类中的每种类型的 ctor 提供包装器。

我最近遇到了boost'senable_if这似乎可以满足我的需要。但我无法让它工作。我累了:

 const Matrix op_My( typename enable_if<boost::is_complex<Scalar> >::type* dummy = 0 ) { return g*H_sum_S[2]; };

这是类声明中的一行,g++说:

 error:   expected a constant of type ‘bool’, got ‘boost::is_complex<Scalar>’

我的问题是,这是实现我所追求的好技术吗?以及我应该怎么写。我尝试关注http://www.boost.org/doc/libs/1_54_0/libs/utility/enable_if.html。我使用 gcc 4.8.1。

4

1 回答 1

3

您得到的具体错误是因为 enable_if 将 bool 作为第一个参数,但您将其传递给了一个类型。

boost::is_complex<Scalar> // <- this is a type 

要获得布尔值(真/假),您需要编写:

boost::is_complex<Scalar>::value // <- this is a bool value telling whether Scalar is complex

is_complex 结构继承自 true_type 或 false_type(取决于标量的类型),如果您想更详细地了解它是如何工作的,请查看它们;)

不过,您的代码还有其他问题。enable_if 需要依赖于调用函数时首先知道的模板参数,而不是类模板参数。你可以这样做:

template<typename Scalar> 
class matrix
{
//...
   public:
   // print function that will be called for a matrix of complex numbers
   template<typename T=Scalar
          , typename std::enable_if<boost::is_complex<T>::value,int>::type = 0
          >
   void print() const;

   // print function that will be called for a matrix of non-complex numbers
   template<typename T=Scalar
          , typename std::enable_if<!boost::is_complex<T>::value,int>::type = 0
          >
   void print() const;
   //...
};

这将产生一个“开关”,根据类模板参数选择适当的类方法。我按照惯例选择将 enable_if 放在模板参数中,而不是放在函数签名中。我发现这是一个更通用的解决方案,并且更具可读性。

这是否是提供此功能的“最佳”方式,我不知道(我无法想到任何主要缺点),但它会解决问题。希望能帮助到你 :)

2013 年 8 月 11 日编辑:

我使用特定类型的 enable_if 结构,因为它允许我在两个函数之间进行 enable_if 切换,否则这两个函数将具有完全相同的签名。使用 enable_if 的“通常”方式之一是使用 enable_if 的结果作为模板参数的默认值,如下所示:

template<typename Scalar> 
class matrix
{
//...
   public:
   // print function that will be called for a matrix of complex numbers
   template<typename T=Scalar
          , class = typename std::enable_if<boost::is_complex<T>::value>::type // enable_if is used to give default type for the class template
          >
   void print() const; 

   // print function that will be called for a matrix of non-complex numbers
   template<typename T=Scalar
          , class = typename std::enable_if<!boost::is_complex<T>::value>::type // same as above
          >
   void print() const; //<- this has same function signature as the above print()
                       //   we get a compiler error
   //...
};

编译器无法区分这两个打印函数,因为它们都以相同的方式模板化,并且具有相同的签名,因为它们都被视为

template<typename T, typename U>
void print() const;

在我的示例中,编译器不知道函数是如何模板化的,因为这是由 enable_if 的结果决定的

template<typename T, ?>
void print() const;

因此可以在调用函数并评估 enable_if 时首先看到函数签名。我按照惯例选择了 int,但你也可以使用 void*:

typename std::enable_if<!boost::is_complex<T>::value,void*>::type = nullptr

使用 void* 而不是 int,但不仅仅是 void,因为我们不能有 void 非类型模板参数。

要做你想做的事,你需要在你的派生类中提供一个非模板函数来覆盖基类中的抽象函数。为此,您可以使用间接并执行以下操作:

class matrix_base
{
   public:
      virtual void print() const = 0;
};

template<typename Scalar>
class matrix: matrix_base
{
   //...
   private:
      // print function that will be called for a matrix of complex numbers
      template<typename T=Scalar
             , typename std::enable_if<boost::is_complex<T>::value,int>::type = 0
             >
      void print_impl() const;

      // print function that will be called for a matrix of non-complex numbers
      template<typename T=Scalar
             , typename std::enable_if<!boost::is_complex<T>::value,int>::type = 0
             >
      void print_impl() const;

   public:
      // print function that will override abstract print in base class
      void print() const { print_impl(); } // <- redirect to one of the print_impl() functions

      //...
};

这种方法的缺点是您需要为每种可能的矩阵类型提供 print_impl() 实现,即您不能只为复数提供 print_impl(),还需要为非复数提供一个。

希望这能让一些事情更清楚:)

于 2013-11-07T21:51:20.327 回答