【问题标题】:overload resolution fails for derived class派生类的重载决议失败
【发布时间】:2015-03-04 16:22:31
【问题描述】:

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

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

另一个类mImage继承自mArray并提供特定于图像的操作,另一个类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&lt;int&gt;,当我使用时

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的加法以及一个值的加法,但当我在主程序中调用+=运算符将两个数组相加时,它使用+=单个值的实现。

我已经尝试了几件事,例如

  • std::enable_if&lt;std::is_arithmetic&lt;U&gt;::value &gt;::type* 用作operator+= 的值版本——不允许,因为operator+= 严格采用1 个参数
  • 同时为mImagemDcmImage 定义operator+= 的两个版本——在这些级别上,它也使用了错误的实现。

simplified example 中选择了正确版本的运算符——为什么不现在呢?我不明白为什么重载解决方案在这里失败。

【问题讨论】:

    标签: c++ templates operator-overloading overloading overload-resolution


    【解决方案1】:

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

    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 也是可能的,例如在返回类型中:

    template <typename U> 
    typename std::enable_if<std::is_arithmetic<U>::value>::type operator+= (const U& rhs);
    

    【讨论】:

    • 为什么不在与mArray&lt;U&gt; const&amp; 兼容的基础上进行测试?如果兼容,请先使用,如果不兼容,请使用第二个。
    • @Yakk is_arithmetic 是 OP 中使用的测试,这也将类限制为数值数据,所以我就去尝试了。
    • 哈!我喜欢仅删除 &lt;typename U&gt; 的第一个解决方案,因为它有效并且比我想象的要简单得多! SFINAE 有什么好处?
    • @alle_meije 两种情况下执行的转换是不同的(一种将操作数转换为T,一种可能没有),所以在某些极端情况下会有差异。 A simplified example.
    【解决方案2】:
    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
    }
    

    【讨论】:

    • 谢谢,我没想过要这样做。 += 运算符不能接受超过 1 个参数,但它当然可以调用这样做的函数。
    • 在尝试这个之后,事实证明对于帖子中的代码,即使用mDcmImage而不是mArray时,测试返回is_mArray&lt;mDcmImage&lt;int&gt;&amp;&gt;又名std::integral_constant&lt;bool,false&gt;并且没有能找到替代品吗?
    • @alle 啊。对不起,我需要一个衰变。添加...应该是固定的。
    猜你喜欢
    • 2011-03-26
    • 1970-01-01
    • 2020-07-19
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    • 2023-03-11
    • 2021-12-23
    相关资源
    最近更新 更多