C++11 C++;0x完美转发会妨碍复制吗?

C++11 C++;0x完美转发会妨碍复制吗?,c++11,C++11,使用MSVC2010 我有一个包装std::string的结构,定义了标准的move-ctor,还有一个完美的forwarding-ctor将参数转发给std::string-ctor struct Wrapper { std::string value; Wrapper() { } Wrapper( Wrapper const& rhs ) :value(rhs.value) { } Wrapper( Wrapper&& r

使用MSVC2010

我有一个包装std::string的结构,定义了标准的move-ctor,还有一个完美的forwarding-ctor将参数转发给std::string-ctor

struct Wrapper
{
  std::string value;

  Wrapper()
  {
  }

  Wrapper( Wrapper const& rhs )
    :value(rhs.value)
  {
  }

  Wrapper( Wrapper&& rhs )
    :value(std::move(rhs.value))
  {
  }

  Wrapper& operator=( Wrapper const& rhs )
  {
    value = rhs.value;
    return *this;
  }

  Wrapper& operator=( Wrapper&& rhs )
  {
    value = std::move(rhs.value);
    return *this;
  }


  template<typename StringT>
  Wrapper( StringT&& value )
    :value(std::forward<StringT>(value))
  {
  }
};
这导致一个指向完美转发构造函数的编译错误,表示它无法将包装转换为std::string。这是正确的行为吗?相对于模板重载,编译器不应该调用复制构造函数吗

1>t:\depot\warp\code\apps\pf_test\main.cpp(56): error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax> &'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          Reason: cannot convert from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          t:\depot\warp\code\apps\pf_test\main.cpp(63) : see reference to function template instantiation 'Wrapper::Wrapper<Wrapper&>(StringT)' being compiled
1>          with
1>          [
1>              StringT=Wrapper &
1>          ]
1>

是的,这是正确的行为

在这种情况下,可能会调用两个构造函数—复制构造函数和模板构造函数。但是,模板构造函数可能会将类型推断为完全匹配(
Wrapper(StringT&&)
with
StringT=Wrapper&
生成完全匹配的
Wrapper(Wrapper&)
),因此使用它来代替复制构造函数

建议的解决方法-如果
中的
std::enable_

模板
包装器(StringT&value、,
typename std::启用\u如果<
!std::是吗<
斯特林特,
包装纸&
>::价值
>::类型*=0)
:值(标准::正向(值))
{ }

看到它在工作。

很有趣。。。因此,在编译器认为我选择的左值不能绑定到右值引用StringT&&之前,就选择了模板重载?我不确定我是否相信这一点,因为错误消息位于字符串ctor点,似乎已经确定参数类型是兼容的?或者我只是对顺序有一个误解,这一切都在左值中得到解决,可以绑定到string&&。由于引用折叠规则,带有
StringT=Wrapper&
(即
Wrapper&&&
)的
StringT&&&
折叠为
Wrapper&
@mikenicolela,模板构造函数将吃掉您试图构造它的非常量引用(Wrapper&)。这就是为什么当您添加包装器(Wrapper&w)时它应该工作的原因。若您使用一个接受多个输入(2个或多个值而不是一个值)的类进行测试,您不会注意到这个问题。啊哈,我错过了t&&实际上可以绑定到左值。嗯。谢谢@MikeNicolella:T&&不能绑定到左值。这里的诀窍在于T的细节——在完美地转发到左值时,它变成(const)T&,其中T是底层的值类型,例如int。因此,实际的引用类型最终是(可能是const)T&,因为((const)T&&)衰减为(const)T&。正如您所知,您不需要手工编写所有这些构造函数。他们添加了使用'Wrapper()=default;'语法。它会给你默认的构造函数和赋值操作符(你需要为你想要的每个ctr/op都这样做)。谢谢你的提示-看来VC2010不支持这个yetSorry,我有时忘记了,因为我主要使用gcc。
1>t:\depot\warp\code\apps\pf_test\main.cpp(56): error C2664: 'std::basic_string<_Elem,_Traits,_Ax>::basic_string(const std::basic_string<_Elem,_Traits,_Ax> &)' : cannot convert parameter 1 from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax> &'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          Reason: cannot convert from 'Wrapper' to 'const std::basic_string<_Elem,_Traits,_Ax>'
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]
1>          No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>          t:\depot\warp\code\apps\pf_test\main.cpp(63) : see reference to function template instantiation 'Wrapper::Wrapper<Wrapper&>(StringT)' being compiled
1>          with
1>          [
1>              StringT=Wrapper &
1>          ]
1>
  Wrapper( Wrapper& rhs )
    :value(rhs.value)
  {
  }
template <typename StringT>
Wrapper(StringT&& value,
  typename std::enable_if<
    !std::is_same<
      StringT,
      Wrapper&
    >::value
  >::type* = 0)
  : value(std::forward<StringT>(value))
{ }