C++ 编写值包装器类的最佳方法

C++ 编写值包装器类的最佳方法,c++,c++11,C++,C++11,Supose我需要编写一个类作为值的包装器: template<typename T> struct value_wrapper { T value; value_wrapper( const T& v ) : value( v ) {} //etc... }; 我的问题是:实现这一目标的最佳(有效)方式是什么? 问题似乎很广泛,我的意思是: 如何将成员值定义为T&表示左值,将T表示右值 有没有标准的方法来编写这种通用(右值和左值)别名 我只需

Supose我需要编写一个类作为值的包装器:

template<typename T>
struct value_wrapper
{
    T value;

    value_wrapper( const T& v ) : value( v ) {}

    //etc...
};
我的问题是:实现这一目标的最佳(有效)方式是什么? 问题似乎很广泛,我的意思是:

  • 如何将成员
    值定义为
    T&
    表示左值,将
    T
    表示右值
  • 有没有标准的方法来编写这种通用(右值和左值)别名
我只需提供合适的转换操作员:

#include <utility>
#include <type_traits>

template <typename T> struct Wrapper
{
    static_assert(!std::is_reference<T>::value, "Do not use a reference type");

    using type = T;

    T value;
    Wrapper(T && t) : value(std::move(t)) {}
    Wrapper(T const & t) : value(t) {}

    operator T const & () const noexcept { return value; }
    operator T       & ()     & noexcept { return value; }
    operator T      && ()    && noexcept { return std::move(value); }

    // maybe some more CV variants...
};

template <typename U> struct Wrapper<U &>
{
    using type = U &;

    U & ref;
    Wrapper(U & u) : ref(u) {}

    operator U & () { return ref; }
};

转换运算符允许您使用在基础类型上定义的任何运算符。

我认为Kerrek SB通过提供专门化(很久以前就获得了my+1)走上了正确的道路,因此每种情况都得到了最有效的处理

问题是你不能只添加隐式转换操作符,如果你想提供你自己的操作符重载,事情会变得相当棘手

我提出的解决方案试图通过将某个变量在哪种情况下的信息放入布尔模板参数来解决这个问题。下面是
value\u包装器
类的基本框架:

template< typename T, bool >
class value_wrapper
{
private:
    T t_; // store a value

public:
    explicit value_wrapper( T&& t ) : t_( std::move( t ) ) {}
    const T& cref() const { return t_; }
};

template< typename T >
struct value_wrapper< T, true > // specialization for lvalue references
{
private:
    const T& t_; // store a reference

public:
    explicit value_wrapper( const T& t ) : t_( t ) {}
    const T& cref() const { return t_; }
};
(显然,您希望以不同的方式实现它们,但始终可以通过
cref()
以统一的方式访问存储的值)


如果您需要非恒定访问等,您可能需要对此进行调整,但我希望以上内容可以帮助您开始。如果您需要更多帮助/想法,请随时提问:)

我想得越多,就越不了解这样一个包装器类想要实现什么。你能详细介绍一下这个用例吗?@DanielFrey,除其他外,我正在考虑编写一组支持浮点的比较函数,我想使用比较运算符。我不喜欢“Java风格”
与(a,b)
相比,我更喜欢操作符重载。在这种情况下,您可能希望避免使用隐式转换运算符,而只将它们标记为
explicit
或通过显式getter函数。然后你必须自己重载操作符,可能使用或my来帮助实现这一点。@DanielFrey我总是(几乎在我可以的时候)使用boost::operators来自动重载操作符:)那么我应该使用什么方法来保持原始值呢?我明天会考虑,我真的累了,现在需要睡觉了。我不理解第二行,在这行中,代码>操作符T &()和NO.{{返回值}} /Cuth>我应该寻找什么C++信息,它是什么?@斯蒂芬纳兰:这是LValk限定符。C++11中新增的,与后续的右值限定符配对。这允许您重载对象本身是左值还是右值。它可以防止什么问题?StfayeloLand:它允许你有效地使用临时包装对象。即使这个类是从我的NOOB的观点来看,令人惊讶的(我不知道之前的限定符,并且我将考虑在未来的转换OPS),这并不能解决我的问题,因为我需要的是重写操作符的行为,而不是包装它。关于你的课程,为什么我需要使用推荐信?谢谢你的回答,我认为这是解决办法。有一个问题:您认为编译器是否可以对所有(或几乎大部分)内联
value\u包装器
机制进行封装,并且只生成用于比较的代码?我的意思是,如果我对
value\u wrapper
有这样一个比较重载:
bool操作符==(…){return compare(lhs.cref(),lhs.cref();}
,并且我在我的示例中使用它(
bool flag=wrap(a)==wrap(b)
),编译器能够生成比
bool flag=compare>复杂得多的代码(a,b);
?@Manu343726如果你复制一份(可能移动到),编译器甚至不允许删除它,除非它能证明没有副作用(在某些情况下这是相当棘手的)。但也请注意,您可能无论如何都不需要这些副本,因为即使是临时变量也只会在它们出现在其中的完整表达式末尾被销毁。如果所有使用看起来都像
wrap(a)==wrap(f())
那么
f()
是否将临时变量作为此临时变量返回(以及对它的引用)并不重要当计算
operator==
时仍然有效。谢谢。我使用GCC Explorer(GCC4.8.1)编译了它,这让我很惊讶,因为即使使用
-O
编译器也会包含所有的内联行(包括值\u包装运算符=body)对于左值和右值。抱歉,还有一个问题:为什么需要衰减?对于数组?存储指向数组的指针而不是数组本身?@Manu343726主要是为了摆脱引用本身
T&
->
T
,否则操作符重载需要更加复杂。对于衰减类型,重载工作ks如果
T
对于
lhs
rhs
都相同,则忽略布尔值。如果不先衰减,则需要4个重载而不是1或其他复杂的方式来处理有效组合(
T,T
T,T&
T&,T&,T
T&,T&
)也就是说,如果您还想支持数组,那么最好不要考虑它,请考虑<代码> STD::ReaveViRebug < /代码>作为替换。
#include <utility>
#include <type_traits>

template <typename T> struct Wrapper
{
    static_assert(!std::is_reference<T>::value, "Do not use a reference type");

    using type = T;

    T value;
    Wrapper(T && t) : value(std::move(t)) {}
    Wrapper(T const & t) : value(t) {}

    operator T const & () const noexcept { return value; }
    operator T       & ()     & noexcept { return value; }
    operator T      && ()    && noexcept { return std::move(value); }

    // maybe some more CV variants...
};

template <typename U> struct Wrapper<U &>
{
    using type = U &;

    U & ref;
    Wrapper(U & u) : ref(u) {}

    operator U & () { return ref; }
};
template <typename T> Wrapper<T> wrap(T && t)
{ return Wrapper<T>(std::forward<T>(t)); }
int n = 10;
bool b = wrap(n) == wrap(5 + 5)
template< typename T, bool >
class value_wrapper
{
private:
    T t_; // store a value

public:
    explicit value_wrapper( T&& t ) : t_( std::move( t ) ) {}
    const T& cref() const { return t_; }
};

template< typename T >
struct value_wrapper< T, true > // specialization for lvalue references
{
private:
    const T& t_; // store a reference

public:
    explicit value_wrapper( const T& t ) : t_( t ) {}
    const T& cref() const { return t_; }
};
// needs a better name and needs to be moved into a "detail" or "impl" namespace
template< typename T >
using helper = value_wrapper< typename std::decay< T >::type, 
                              std::is_lvalue_reference< T >::value >;

template< typename T >
helper< T > wrap( T&& t )
{
    return helper< T >( std::forward< T >( t ) );
}
template< typename T, bool BL, bool BR >
bool operator==( const value_wrapper< T, BL >& lhs, const value_wrapper< T, BR >& rhs )
{
    return lhs.cref() == rhs.cref();
}