RValue、模板解析和复制构造函数(在 Visual C++ 2010 中)

发布于 2024-11-28 20:08:31 字数 3402 浏览 3 评论 0原文

我正在构建一个简单的容器类,但遇到了一些问题(重新组装 Visual C++ 中的问题2010,右值引用错误?

#include <cassert>
#include <utility>

template<typename T0>
class MyType {
 public:
  typedef T0 value_type;

  // Default constructor
  MyType() : m_value() {
  }

  // Element constructor
  explicit MyType(const T0 &c_0) : m_value(c_0) {
  }

  template<typename S0>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

  // Copy constructor
  MyType(const MyType &other) : m_value(other.m_value) {
  }

  MyType(MyType &&other) : m_value(std::forward<value_type>(other.m_value)) {
  }

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

  // Assignment operators
  MyType &operator=(const MyType &other) {
    m_value = other.m_value;
    return *this;
  }

  MyType &operator=(MyType &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  template<typename S0>
  MyType &operator=(const MyType<S0> &other) {
    m_value = other.m_value;
    return *this;
  }

  template<typename S0>
  MyType &operator=(MyType<S0> &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  // Value functions
  value_type &value() {
    return m_value;
  }

  const value_type &value() const {
    return m_value;
  }

 private:
  template<typename S0>
  friend class MyType;

  value_type m_value;
};

int main(int argc, char **argv) {
  MyType<float>  t1(5.5f);
  MyType<double> t2(t1);

    return 0;
}

上面的代码给出了以下错误:

1>ClCompile:
1>  BehaviorIsolation.cpp
1>behaviorisolation.cpp(18): error C2440: 'initializing' : cannot convert from 'MyType<T0>' to 'double'
1>          with
1>          [
1>              T0=float
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          behaviorisolation.cpp(78) : see reference to function template instantiation 'MyType<T0>::MyType<MyType<float>&>(S0)' being compiled
1>          with
1>          [
1>              T0=double,
1>              S0=MyType<float> &
1>          ]
1>behaviorisolation.cpp(18): error C2439: 'MyType<T0>::m_value' : member could not be initialized
1>          with
1>          [
1>              T0=double
1>          ]
1>          behaviorisolation.cpp(73) : see declaration of 'MyType<T0>::m_value'
1>          with
1>          [
1>              T0=double
1>          ]
1>
1>Build FAILED.

如何在不使用链接问题中描述的技巧的情况下纠正此错误?

谢谢!

编辑: 最让我困惑的是为什么没有调用两个专门的构造函数中的任何一个。他们更适合这个电话。

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

I'm constructing a simple container class but run into some problems (reassembling the ones in Visual C++ 2010, rvalue reference bug?)

#include <cassert>
#include <utility>

template<typename T0>
class MyType {
 public:
  typedef T0 value_type;

  // Default constructor
  MyType() : m_value() {
  }

  // Element constructor
  explicit MyType(const T0 &c_0) : m_value(c_0) {
  }

  template<typename S0>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

  // Copy constructor
  MyType(const MyType &other) : m_value(other.m_value) {
  }

  MyType(MyType &&other) : m_value(std::forward<value_type>(other.m_value)) {
  }

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

  // Assignment operators
  MyType &operator=(const MyType &other) {
    m_value = other.m_value;
    return *this;
  }

  MyType &operator=(MyType &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  template<typename S0>
  MyType &operator=(const MyType<S0> &other) {
    m_value = other.m_value;
    return *this;
  }

  template<typename S0>
  MyType &operator=(MyType<S0> &&other) {
    m_value = std::move(other.m_value);
    return *this;
  }

  // Value functions
  value_type &value() {
    return m_value;
  }

  const value_type &value() const {
    return m_value;
  }

 private:
  template<typename S0>
  friend class MyType;

  value_type m_value;
};

int main(int argc, char **argv) {
  MyType<float>  t1(5.5f);
  MyType<double> t2(t1);

    return 0;
}

The above code gives the following error:

1>ClCompile:
1>  BehaviorIsolation.cpp
1>behaviorisolation.cpp(18): error C2440: 'initializing' : cannot convert from 'MyType<T0>' to 'double'
1>          with
1>          [
1>              T0=float
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          behaviorisolation.cpp(78) : see reference to function template instantiation 'MyType<T0>::MyType<MyType<float>&>(S0)' being compiled
1>          with
1>          [
1>              T0=double,
1>              S0=MyType<float> &
1>          ]
1>behaviorisolation.cpp(18): error C2439: 'MyType<T0>::m_value' : member could not be initialized
1>          with
1>          [
1>              T0=double
1>          ]
1>          behaviorisolation.cpp(73) : see declaration of 'MyType<T0>::m_value'
1>          with
1>          [
1>              T0=double
1>          ]
1>
1>Build FAILED.

How can one remedy this error without using tricks like the ones described in the linked question?

Thanks!

Edit:
What confuses me the most is why isn't any of the two specialized constructors called. They fit the call a lot better.

  // Copy constructor (with convertion)
  template<typename S0>
  MyType(const MyType<S0> &other) : m_value(other.m_value) {
  }

  template<typename S0>
  MyType(MyType<S0> &&other) : m_value(std::move(other.m_value)) {
  }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

甚是思念 2024-12-05 20:08:31

您链接的问题已经回答了这个问题。让我们定义

typedef MyType<float>  MF;
typedef MyType<double> MD;

当您说 MD t2(t1); 时,您喜欢调用构造函数 MF::MF(const MD &) 。然而,构造函数 templateMF::MF(T&&) 匹配得更好,因为它需要 T = MD& 并因此解析为 MF::MF(MD&) ,由于缺少 const,这是一个更好的匹配。

要解决此问题,您应该基本上摆脱 MF(T&&) 构造函数,正如 Howard 已经建议的那样。由于无论如何您都只打算使用值的构造函数,因此我的第一个建议是将签名更改为 MF(const T &),这已经可以解决您的问题。另一种解决方案是添加带有签名 MF(MD&) (非常量)的构造函数。但这很丑。最后,您可以在调用站点显式调用构造函数:MD t2(MF(t1))MD t2(std::forward(t1)) 甚至 MD t2(std::move(t1))(如果可以的话)。

最后请注意,如果您只处理原始成员,则从显式移动中不会获得任何好处,因此您最好不要单独定义所有这些构造函数。

Your linked question already answers this. Let's define

typedef MyType<float>  MF;
typedef MyType<double> MD;

When you say MD t2(t1);, you would like to call the constructor MF::MF(const MD &). However, the constructor template <typename T> MF::MF(T&&) matches better, because it takes T = MD& and thus resolves to MF::MF(MD&), which is a better match due to the absence of const.

To resolve this, you should essentially get rid of the MF(T&&) constructor, as Howard suggested already. Since you only intend that constructor for values anyway, my first suggestion would be to change the signature to MF(const T &), which would already solve your problem. Another solution would be to add a constructor with signature MF(MD&) (non-const). That's ugly, though. Finally, you could call the constructor explicity at the call site: MD t2(MF(t1)), or MD t2(std::forward<MF>(t1)) or even MD t2(std::move(t1)), if that's an option.

Finally note that if you are only dealing with primitive members, there's nothing to be gained from explicit moves, so you might as well not bother defining all those constructors separately.

海的爱人是光 2024-12-05 20:08:31

您的构造函数:

 template<typename S0>
  explicit MyType(S0 &&c_0)

过于通用,解决问题的最佳方法是限制可以推导为 S0 的类型。这基本上就是链接答案的作用。但也许我可以为你让它变得更漂亮。

就是这样,在 std::C++11 中:

  template<typename S0,
           class = typename std::enable_if
           <
               std::is_convertible<S0, T0>::value
           >::type>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

如果这太丑陋了,您可能会考虑:

#define restrict_to(x...) class = typename std::enable_if<x>::type

...

template<typename S0, restrict_to(std::is_convertible<S0, T0>::value)>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}

请注意,这不仅会解决您的问题,而且如果您的客户随后提出以下问题:

std::is_convertible<X, MyType<T>>::type

他们现在会得到正确答案。正如您当前所编码的那样,上述特征始终的答案为真。

Your constructor:

 template<typename S0>
  explicit MyType(S0 &&c_0)

is overly generic and the best way to solve your problem is to restrict the type which can be deduced as S0. This is basically what the linked answer does to. But perhaps I can make it prettier for you.

Here it is, in std::C++11:

  template<typename S0,
           class = typename std::enable_if
           <
               std::is_convertible<S0, T0>::value
           >::type>
  explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
  }

If that is just too ugly, you might consider:

#define restrict_to(x...) class = typename std::enable_if<x>::type

...

template<typename S0, restrict_to(std::is_convertible<S0, T0>::value)>
explicit MyType(S0 &&c_0) : m_value(std::forward<S0>(c_0)) {
}

Note that this will not only solve your problem, but if your clients subsequently ask questions like:

std::is_convertible<X, MyType<T>>::type

they will now get the correct answer. As you currently have it coded, the above trait always answers true.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文