C++ 中重载乘法运算符的递归 class 模板实例化期间出错

Error during recursive class template instantiation for overloaded multiplication operator in C++

我正在尝试为一个非常特殊的框实现模板 class,称为 H Box
它包含一个数字向量,其大小始终为 2 的幂。当涉及到结构时,本质上就是它 - 它仅包含一个数组,并重载了 [] 运算符以访问其元素。问题是 - 它有一个非常奇特的乘法规则:两个 H Boxes 的乘积归结为将向量分成左半部分和右半部分,创建更小的盒子,将它们交叉相乘,然后最后总结结果(下面代码中的详细信息)。所以问题是乘法运算符被重载为递归函数,它生成越来越小的框。

我的 class 的非模板版本的代码已经可用。但是,在我移动到模板后,我无法正确...

template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> &H1,
    const HBox<T, size> &H2
) {
    // recursion base:
    if (size == 1) {
        T temparr[] = { H1[0] * H2[0] };
        HBox<T, 1> H_(temparr);
        return H_;
    }
    // shared objects:
    const unsigned int halfsize = size / 2;
    T* temparr = new T[size];
    // construct helper objects:
    for (unsigned int i=0; i < halfsize; i++) temparr[i] = H1[i];
    HBox<T, halfsize> H1a(temparr);
    for (unsigned int i=0; i < halfsize; i++) temparr[i] = H1[i+halfsize];
    HBox<T, halfsize> H1b(temparr);
    for (unsigned int i=0; i < halfsize; i++) temparr[i] = H2[i];
    HBox<T, halfsize> H2a(temparr);
    for (unsigned int i=0; i < halfsize; i++) temparr[i] = H2[i+halfsize];
    HBox<T, halfsize> H2b(temparr);
    // multiply recursively:
    HBox<T, halfsize> H1a2a = H1a * H2a;
    HBox<T, halfsize> H2b1b = H2b * H1b;
    HBox<T, halfsize> H2b1a = H2b * H1a;
    HBox<T, halfsize> H1b2a = H1b * H2a;
    // construct the final object
    HBox<T, halfsize> Ha = H1a2a + H2b1b;
    HBox<T, halfsize> Hb = H2b1a + H1b2a;
    for (unsigned int i=0; i < halfsize; i++) temparr[i] = Ha[i];
    for (unsigned int i=0; i < halfsize; i++) temparr[i+halfsize] = Hb[i];
    HBox<T, size> H(temparr);
    delete[] temparr;
    return H;
}

我编译了一个简单的测试程序,其中包含两个四元素 float 框 (A * B) 的乘积:

Run g++ -O0 -Wall --std=c++14 -o test test.cpp

我收到以下错误:

In file included from test.cpp:17:
HBox/HBox.hpp: In instantiation of ‘HBox<T, size> operator*(const HBox<T, size>&, const HBox<T, size>&) [with T = float; unsigned int size = 4]’:
test.cpp:266:44:   required from here
HBox/HBox.hpp:276:16: error: could not convert ‘H_’ from ‘HBox<[...],1>’ to ‘HBox<[...],4>’
  276 |         return H_;
      |                ^~
      |                |
      |                HBox<[...],1>
Error: Process completed with exit code 1.

出了什么问题?在我看来,这里应该一切都很好...

使用模板递归时,您不能将基本情况作为简单 if 提供。这样做的原因是即使某些代码无法访问,整个函数也需要能够编译。一个简单的if (false)是不够的。

要解决此问题,您可以使用 C++17 的 if constexpr or partial template specialization:

// using if constexpr
template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> &H1,
    const HBox<T, size> &H2
) {
    if constexpr (size == 1) {
        // handle base case here
    } else {
        // handle recursive case here
    }
}

// using partial template specialization
template <typename T, const unsigned int size>
HBox<T, size> operator*(
    const HBox<T, size> &H1,
    const HBox<T, size> &H2
) {
    // handle recursive case here
}

template <typename T>
HBox<T, 1> operator*<T, 1>( // partial specialization for size == 1
    const HBox<T, 1> &H1,
    const HBox<T, 1> &H2
) {
    // handle base case here
}

即使您的程序在 n != 1 时永远不会进入 if 语句,您的编译器也会尝试编译这样的分支。因此,您可以从必须 return HBox<T, 4> 的函数中 return HBox<T, 1>。 首先尝试将 constexpr 添加到 if

if constexpr (size == 1)...

如果没有帮助,请尝试使用此代码

HBox<T, size> operator*(const HBox<T, size> &H1, const HBox<T, size> &H2);

template <typename T>
HBox<T, 1> operator*(const HBox<T, 1> &H1, const HBox<T, 1> &H2) {
  T temparr[] = {H1[0] * H2[0]};
  HBox<T, 1> H_(temparr);
  return H_;
}

template <typename T, const unsigned int size>
HBox<T, size> operator*(const HBox<T, size> &H1, const HBox<T, size> &H2) {
  // shared objects:
  const unsigned int halfsize = size / 2;
  T *temparr = new T[size];
  // construct helper objects:
  for (unsigned int i = 0; i < halfsize; i++) temparr[i] = H1[i];
  HBox<T, halfsize> H1a(temparr);
  for (unsigned int i = 0; i < halfsize; i++) temparr[i] = H1[i + halfsize];
  HBox<T, halfsize> H1b(temparr);
  for (unsigned int i = 0; i < halfsize; i++) temparr[i] = H2[i];
  HBox<T, halfsize> H2a(temparr);
  for (unsigned int i = 0; i < halfsize; i++) temparr[i] = H2[i + halfsize];
  HBox<T, halfsize> H2b(temparr);
  // multiply recursively:
  HBox<T, halfsize> H1a2a = H1a * H2a;
  HBox<T, halfsize> H2b1b = H2b * H1b;
  HBox<T, halfsize> H2b1a = H2b * H1a;
  HBox<T, halfsize> H1b2a = H1b * H2a;
  // construct the final object
  HBox<T, halfsize> Ha = H1a2a + H2b1b;
  HBox<T, halfsize> Hb = H2b1a + H1b2a;
  for (unsigned int i = 0; i < halfsize; i++) temparr[i] = Ha[i];
  for (unsigned int i = 0; i < halfsize; i++) temparr[i + halfsize] = Hb[i];
  HBox<T, size> H(temparr);
  delete[] temparr;
  return H;
}```