std::enable_if 在将错误的类型(双精度)而不是 std::complex 传递给它时无法执行操作

std::enable_if fails to act when the wrong type (double) is passed to it instead of std::complex

我想让我的数组 class 将数据从 double 数组复制到 std::complex<double>(或任何 U 到 std::complex)数组 operator= .为此,我使用了 std::enable_if。但是,它未能检测到不应使用它。

以下函数,如果类型 T 很复杂,我想调用它。所以我使用了条件std::is_same<T, std::complex<typename T::value_type>,因为std::complex存储了它使用的类型在::value_type中。这是函数的定义:

template <typename T, int S>
typename std::enable_if<std::is_same<T, std::complex<typename T::value_type> >::value, MyArray<T,S>& >::type
MyArray<T,S>::operator=(const typename std::enable_if<std::is_scalar<typename T::value_type>::value, MyArray<typename T::value_type>&, S >::type rhs)

但是,如果我尝试复制 MyArray<double>,则会出现以下错误。所以显然它没有检测到 double 不是 `

error: 'double' is not a class, struct, or union type MyArray::operator=(const typename std::enable_if::value, MyArray&, S >::type rhs)

我做错了什么?


更新:

我想弄清楚我想要实现的目标,因为(抱歉)有太多的混乱。

我需要这个操作成为可能:

MyArray<double> d;
MyArray<std::complex<double>> cd;
cd = d;

不确定是否理解,但是...如果您只想在传入的 MyArray<T>Tstd::complex<U> 时启用您的 operator=(),为什么不'你只要写

   template <typename F>
     MyArray & operator= (MyArray<std::complex<F>> const & rhs)
      { return *this }

--编辑--

I want to assign U to std::complex<U>, so MyArray<std::complex<U>> = MyArray<U>.

所以你想要的恰恰相反。

我想你可以做类似的事情

#include <complex>
#include <type_traits>

template <typename T>
struct MyArray
 {
   template <typename U>
   typename std::enable_if<std::is_same<T, std::complex<U>>::value,
         MyArray &>::type 
      operator= (MyArray<U> const & rhs)
       { return *this; }
 };

int main()
 {
   MyArray<double> d;
   MyArray<std::complex<double>> cd;
   cd = d;
 }

-- 编辑 2 --

but there's a second template parameter that I removed in the original question, thinking that I'm simplifying the issue for readability. But it was wrong of me to do that, because partial specializations of functions are not allowed in C++. So my template is template , not template , which is very different

我不认为这是必要的偏特化

#include <complex>
#include <type_traits>

template <typename T, int S>
struct MyArray
 {
   template <typename U>
   typename std::enable_if<std::is_same<T, std::complex<U>>::value,
        MyArray &>::type 
    operator= (MyArray<U, S> const & rhs)
     { return *this; }
 };

int main()
 {
   MyArray<double, 3> d;
   MyArray<std::complex<double>, 3> cd;
   cd = d;
 }

如果问题出在 class 正文之外定义 operator(),我提出以下修改示例

#include <complex>
#include <type_traits>

template <typename T, int S>
struct MyArray
 {
   template <typename U>
   typename std::enable_if<std::is_same<T, std::complex<U>>::value,
        MyArray &>::type 
    operator= (MyArray<U, S> const & rhs);
 };

template <typename T, int S>
template <typename U>
typename std::enable_if<std::is_same<T, std::complex<U>>::value,
      MyArray<T, S> &>::type 
   MyArray<T, S>::operator= (MyArray<U, S> const & rhs)
      { return *this; }

int main()
 {
   MyArray<double, 3> d;
   MyArray<std::complex<double>, 3> cd;
   cd = d;
 }

-- 编辑 3 --

Is there a way to do the same with the copy constructor? [...] It's quite tricky and seems impossible because there's no return type.

是的,有办法

#include <complex>
#include <type_traits>

template <typename T, int S>
struct MyArray
 {
   template <typename U>
   typename std::enable_if<std::is_same<T, std::complex<U>>::value,
         MyArray &>::type 
      operator= (MyArray<U, S> const & rhs)
       { return *this; }

   template <typename U,
            typename = typename std::enable_if<std::is_same<T,
                           std::complex<U>>::value>::type>
   MyArray (MyArray<U, S> const & rhs)
    { }

   MyArray() = default;
   MyArray(MyArray const &) = default;
   MyArray(MyArray &&) = default;
   ~MyArray() = default;
 };

int main()
 {
   MyArray<double, 3> d;                      // need MyArray() = default
   MyArray<double, 3> d2(d);                  // OK
   MyArray<float, 3> f;                       // OK
   MyArray<std::complex<double>, 3> cd(d);    // OK
   //MyArray<std::complex<double>, 3> cd2(f); // error!
   cd = d;

 }

观察 = default 添加的行;没有第一个(我不知道其他三个是否有用)代码无法编译,因为(如果我理解正确的话)SFINAE 复制构造函数禁用(删除)默认复制构造函数因此删除其他默认构造函数和默认析构函数。

我发现在尝试理解模板问题时有帮助的是自己尝试替换:

template <double>
typename std::enable_if<std::is_same<double, std::complex<typename double::value_type> >::value, MyArray<double>& >::type
MyArray<double>::operator=(const typename std::enable_if<std::is_scalar<typename double::value_type>::value, MyArray<typename double::value_type>& >::type rhs)

现在很明显了吗?如果 double 是您的 MyArray 类型 T,那么 T 将用于上面的 SFINAE 逻辑。但是 double 没有 ::value_type.

您可以尝试这样的操作:

template<typename T>
typename std::enable_if<std::is_scalar<T>, MyArray<T>&>::type
  MyArray<T>::operator=(const std::complex<T>& rhs){...}

如果您不需要 is_scalar 检查,请改用 max66 的答案。

更新:
好的,好的。所以这个应该地址想要通过赋值运算符从MyArray<T>MyArray<std::complex<T>>而不是相反。

template<typename T, typename U>
typename std::enable_if<std::is_same<T, std::complex<U>>::value && std::is_scalar<U>::value, MyArray<T>&>::type
  MyArray<T>::operator=(const MyArray<U>& rhs){...}

这里的技巧是 intstd::complex<int> 不是同一类型。您也可以通过将 T::value_type 替换为 U 来实现。最后,您可以通过针对 MyArray<std::complex<T>> class 的情况部分专门化您的 MyArray<T> class 来做到这一点,如果您发现自己需要更多类似您所要求的东西, 可能值得考虑。