I have a numerical library that has been designed with one “flavor” in mind. Now I want to generalize this. The basic data structure is a “spinor” which itself is a multi dimensional matrix. There are lots of functions which take arrays of these spinors. The generalized functions need to take one such spinor array for each flavor.
Say there is a function which, minimally, does the following:
void legacy(Spinor *out, const Spinor *in) {}
My generalization now is this:
void legacy(Spinor *out[num_flav], const Spinor *in[num_flav]) {}
As far as I understand, one has to read this as const Spinor *(in[num_flav])
, so in
is a pointer to an array of probably num_flav
elements (or another quantity because foo[]
is just *foo
in a function parameter) of type pointer-to-const-spinor.
The problem is that it does not compile when using a Spinor *non_const[2]
(without the const
), see my earlier question. From the answer there I have learned that this must not compile because within the function legacy
, the pointer non_const[0]
could be made to point to some const
array of Spinor *
. Then non_const
would point to const
data. Therefore that does not work.
My conclusion was that adding another const
will make it correct:
void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
When I now pass my non_const
as the second parameter, the function cannot change in[0]
to anything, because that pointer is now immutable. This has served me well with GCC 6.3. Now going into production with Intel C++ 17, it does not work any more.
The minimal working example is the following:
#include <cstdint>
typedef float Spinor[3][4][2][8];
template <uint8_t num_flav>
class Solver {
public:
void legacy(Spinor *out, const Spinor *in) {}
void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
};
int main(int argc, char **argv) {
Spinor *s1 = new Spinor[10];
Spinor *s2 = new Spinor[10];
Spinor *s1_a[1] = {s1};
Spinor *s2_a[1] = {s2};
Solver<1> s;
s.legacy(s2_a, s1_a);
}
On GCC, it apparently resolves to the second legacy
overload. The variable s1_a
which takes the role of the previous non_const
, is allowed as an argument.
Intel C++ 17 however, does not accept that:
$ icpc -Wall -pedantic const-spinor-const.cpp --std=c++11
const-spinor-const.cpp(23): error: no instance of overloaded function "Solver<num_flav>::legacy [with num_flav=(uint8_t={unsigned char})'\001']" matches the argument list
argument types are: (Spinor *[1], Spinor *[1])
object type is: Solver<(uint8_t)'\001'>
s.legacy(s2_a, s1_a);
^
const-spinor-const.cpp(11): note: this candidate was rejected because arguments do not match
void legacy(Spinor *out[num_flav], const Spinor *const in[num_flav]) {}
^
const-spinor-const.cpp(10): note: this candidate was rejected because arguments do not match
void legacy(Spinor *out, const Spinor *in) {}
^
The error message is not particularly helpful because it does not say what conversion was not allowed. It just seems that the const
is the problem.
When I change the definition of s1_a
in the main
function to have the two const
, it compiles:
const Spinor *const s1_a[1] = {s1};
In order to run the code in production, I see the following options:
-
Add a
const
wrapper for every array that I pass to my generalized functions. That will look pretty bad and is not really easy to maintain. -
Remove the left-most
const
from the function argument parameters. That compiles cleanly on both compilers. However, I do want to document that I am not changing anything in that array, therefore its values should beconst
.
Is there perhaps something I miss with Intel C++? Is it lacking a feature or did I make use of an unofficial GCC extension? Is that a bug in Intel C++ or GCC?
What would be a good way to go forward? I can alter my new functions all I want, but I would like to avoid touching caller code as much as possible.
Aucun commentaire:
Enregistrer un commentaire