派生 class 的重载解析失败

overload resolution fails for derived class

我正在使用 class mArray,它实现了具有可变维度的数值数据容器

template <typename T>
   class mArray<T>: {
     ...
     std::vector<T> my_data;
   }

另一个 class mImage 继承自 mArray 并提供图像特定的操作,另一个 class mDcmImage 继承自 mImage 和提供特定于格式的功能。

我认为这是一种分离不同类型功能的非常简洁的方法。

一个示例或 mArray 功能:逐元素加法:

// 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() 是一个 mArray 函数)
但是现在我的代码从文件中加载 mDcmImage<int>,当我使用

typedef int intensity;
mDcmImage<intensity> im1 ("/tmp/test1.im");
mDcmImage<intensity> im2 ("/tmp/test2.im");
im1 += im2;

然后我得到以下错误:

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’|

换句话说:虽然我编写了另一个 mArray 的加法以及一个值的加法,但是当我在主程序中调用 += 运算符将两个数组加在一起时,它对单个值使用 += 实现。

我试过好几种,比如

中选择了 运算符的正确版本 -- 为什么现在不选择?我不明白为什么重载决议在这里失败了。

模板参数推导后,第二个版本完美匹配:

template <typename U> void operator+= (const U& rhs); // U = mDcmImage<int>

与需要从 mDcmImage 转换为 mArray 的第一个版本相比:

template <typename U> void operator+= (const mArray<U>& other); // U = int

所以它是通过重载决议选择的。


最简单的修复可能是修改单值版本以仅采用 T 并依赖隐式转换:

void operator+= (const T& rhs);

SFINAE 也是可以的,例如在 return 类型中:

template <typename U> 
typename std::enable_if<std::is_arithmetic<U>::value>::type operator+= (const U& rhs);
namespace details {
  template<template<class...>class Z>
  std::false_type inherits_from_template_helper(...);
  template<template<class...>class Z, class...Us>
  std::true_type  inherits_from_template_helper(Z<Us...>&&);

}
// C++14 has this in `std`:
template<class T>using decay_t=typename std::decay<T>::type;
template<template<class...>class Z, class T>
using inherits_from_template
  = decltype(
    details::inherits_from_template_helper<Z>(
      std::declval<decay_t<T>>()
    )
  );

template<class T>
using is_mArray = inherits_from_template<mArray, T>;

是一个特征测试,表示“传入的类型基本上是 mArray

现在使用 SFINAE 或标签分派来分派以更正 +=,具体取决于您的参数是否为 mArray

template<class U>
mArray<T>& operator+=(U&& u) {
  increase_by( std::forward<U>(u), is_mArray<U>{} );
  return *this;
}
template<class U>
void increase_by( U const& u, std::false_type /* is_mArray<U> */ ) {
  // scalar addition code
}
template<class U>
void increase_by( mArray<U> const& u, std::true_type /* is_mArray<U> */ ) {
  // mArray addition code
}