Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.3k views
in Technique[技术] by (71.8m points)

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

I am trying to implement a template class for a very special box, called H Box.
It holds a vector of numbers with the size being always a power of 2. That is essentially it, when it comes to the structure - it only contains an array and has the [] operator overloaded to access its elements. The thing is - it has a very fancy multiplication rules: the product of two H Boxes boils down to splitting vectors to left- & right- halves, creating smaller boxes, cross-multiplying them and summing up the results in the end (details in the code below). So the issue is that the multiplication operator is overloaded as a recursive function which generates smaller and smaller boxes.

I have had the code working for the non-template version of my class. However, after I moved to template I cannot get it right...

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

I compile a simple test program which contains a multiplication of two four-elemental float boxes (A * B) with:

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

And I get the following error:

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.

What has gone wrong? It seems to me that everything should be fine here...


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

When using template recursion you can't provide the base case as a simple if. The reason for this is that the whole function needs to be able to compile even if some code is unreachable. A simple if (false) is not enough.

To fix this you can either use C++17's 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
    }
}

or

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...