C++ 包装对象的语义:默认情况下通过std::move/std::ref引用/值

C++ 包装对象的语义:默认情况下通过std::move/std::ref引用/值,c++,c++11,stl,boost-fusion,reference-wrapper,C++,C++11,Stl,Boost Fusion,Reference Wrapper,最近,我经常使用我在C++11中“发现”的一个自然习惯用法,即包装对象可以在可能的情况下自动保存引用。这里的主要问题是如何将此“习惯用法”的行为与标准中的其他行为进行比较(见下文) 例如: template<class T> struct wrap{ T t; }; template<class T> wrap<T> make_wrap(T&& t){ return wrap{std::forward<T>(t)};

最近,我经常使用我在C++11中“发现”的一个自然习惯用法,即包装对象可以在可能的情况下自动保存引用。这里的主要问题是如何将此“习惯用法”的行为与标准中的其他行为进行比较(见下文)

例如:

template<class T>
struct wrap{
    T t;
};
template<class T> wrap<T> make_wrap(T&& t){
    return wrap{std::forward<T>(t)};
}
typeid( std::make_pair(3.14, 3.14) ) --> std::pair<double, double>
typeid( std::make_pair(a, a) ) --> std::pair<double, double> // noref
typeid( std::make_pair(c, c) ) --> std::pair<double, double> // noref
我知道

typeid( make_wrap(3.14) ) --> wrap<double>
typeid( make_wrap(a) ) --> wrap<double&>
typeid( make_wrap(c) ) --> wrap<double const&>
参考文件:

typeid( std::make_pair(std::ref(a), std::ref(a) ) ) --> std::pair<double&, double&> // ref
typeid( std::make_pair(std::ref(c), std::ref(c) ) ) --> std::pair<double const&, double const&> // const ref
这似乎允许我在不相关的类型
wrap
wrap
之间进行复制:

boost::fusion::make_vector(5,a)
的类型是
boost::fusion::vector2
。好的

boost::fusion::make_vector(5.,boost::ref(a)))的类型是
boost::fusion::vector2`。好的,如文件所述

然而,令人惊讶的是,Boost.Fusion并不是用C++11 STL编写的,我们得到:
boost::fusion::make_vector(5.,std::ref(a))
的类型为
boost::fusion::vector2
。惊喜


本节旨在说明当前STL行为取决于协议(例如,用于标记引用的类),而使用
std::move
(或者更准确地说是右值转换)的另一个(我称之为“自然”行为)不依赖于协议,但它更为(当前)本地语言。

我认为a)值语义(在大多数标准库中,copy=default)b)悬挂引用是危险的,因此您应该明确引入它们。丢弃引用的行为(除非给定
引用\u包装器
可能更复杂,但我认为这会使使用变得更容易,因为你不必担心悬挂引用,除非你通过使用
std::ref
@Praetorian和DyP明确表示,很长一段时间以来,我一直认为悬挂引用最终会咬到我,但到目前为止,我发现这是非常自然的,我不必担心非自愿复制。也许我的使用是有偏见的,因为我处理了很多不可修改的大对象(修改比复制要昂贵的东西,例如插值)。也许这与在上下文中使用更具功能性的编程风格有关。@Yakk函数
copy
不会解决这个问题吗<代码>模板T复制(T const&p){return T(p);}
包装(复制(a))
--vs.
make_pair(move(a))
@alfC“不做X”其中X没有开销不同于“你不能做Y”,或者“你只能做Y,Y意味着不可避免的开销”。限制是不对称的。如果不执行
复制
(到临时文件中)和
移动
,就无法说“我想在
包装中按值存储”。无论如何,
make\u pair
是在右值引用之前设计的。
typeid( std::make_pair(3.14, 3.14) ) --> std::pair<double, double>
typeid( std::make_pair(a, a) ) --> std::pair<double, double> // noref
typeid( std::make_pair(c, c) ) --> std::pair<double, double> // noref
typeid( std::make_pair(std::ref(a), std::ref(a) ) ) --> std::pair<double&, double&> // ref
typeid( std::make_pair(std::ref(c), std::ref(c) ) ) --> std::pair<double const&, double const&> // const ref
template<class T>
struct wrap{
    T t;
    // "generalized constructor"? // I could be overlooking something important here
    template<class T1 = T> wrap(wrap<T1> const& w) : t(std::move(w.t)){}
    wrap(T&& t_) : t(std::move(t)){} // unfortunately I now have to define an explicit constructor
};
auto mw = make_wrap(a);
wrap<double const&> copy0 =mw;
wrap<double&> copy1 = mw; //would be an error if `a` is `const double`, ok
wrap<double> copy2 = mw;
double a;