I am using a class mArray, which implements numerical data containers with variable dimensionality
template <typename T>
class mArray<T>: {
...
std::vector<T> my_data;
}
Another class mImage
inherits from mArray
and provides image-specific operations, and another class mDcmImage
inherits from mImage
and provides format-specific functions.
I thought this was a very clean way to separate different types of functionality.
One example or mArray
functionality: element-wise addition:
// add a mArray to the data [1]
template <typename U>
void operator+= (const mArray<U>& other) {
if (other.getSizes() != getSizes())
throw std::invalid_argument {"incompatible sizes"};
else {
std::transform (my_data.begin(),
my_data.end(),
my_data.begin(),
my_data.begin(),
[]( const T &a, const U &b) { return a+b; } );
}
return;
}
// add an element to the data [2]
template <typename U>
void operator+= (const U& rhs) {
assert (!std::is_arithmetic<U>::value);
std::transform (my_data.begin(),
my_data.end(),
my_data.begin(),
[&rhs]( const T &lhs) { return lhs+rhs; } );
return;
}
(getSizes()
is an mArray
function)
But now my code loads a mDcmImage<int>
from a file, and when I use
typedef int intensity;
mDcmImage<intensity> im1 ("/tmp/test1.im");
mDcmImage<intensity> im2 ("/tmp/test2.im");
im1 += im2;
then I get the following error:
mArray.hpp required from ‘struct mArray<T>::operator+=(const U&)
[with U = mDcmImage<int>; T = int]::<lambda(const int&)>’|
mArray.hpp required from ‘void mArray<T>::operator+=(const U&)
[with U = mDcmImage<int>; T = int]’|
test.cpp required from here|
mArray.hpp error: no match for ‘operator+’ in ‘lhs + rhs’|
In other words: although I coded the addition of another mArray
as well as the addition of one value, when I call the +=
operator in the main program to add two arrays together, it uses the +=
implementation for a single value.
I have tried several things, such as
std::enable_if<std::is_arithmetic<U>::value >::type*
for the value version of operator+=
-- not allowed because operator+=
takes strictly 1 argumentoperator+=
for mImage
and mDcmImage
as well -- at those levels it also uses the wrong implementation.In a simplified example the right version of the operator is selected -- why not now? I cannot see why the overload resolution fails here.
After template argument deduction, the second version becomes a perfect match:
template <typename U> void operator+= (const U& rhs); // U = mDcmImage<int>
compared to the first version, which requires a conversion from mDcmImage
to mArray
:
template <typename U> void operator+= (const mArray<U>& other); // U = int
so it is selected by overload resolution.
The simplest fix is probably to modify the single-value version to just take a T
and rely on implicit conversions:
void operator+= (const T& rhs);
SFINAE is also possible, for instance in the return type:
template <typename U>
typename std::enable_if<std::is_arithmetic<U>::value>::type operator+= (const U& rhs);