2014-01-04 2 views
1

Supose мне нужно написать класс, который действует как оболочка для значений:Лучший способ написать значение обертку класс

template<typename T> 
struct value_wrapper 
{ 
    T value; 

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

    //etc... 
}; 

Класс dessigned будет использоваться в качестве псевдонима от исходного значения, так что если это значение было rvalue, обертка содержит значение, а если это значение lvalue, то обертка содержит ссылку на него. Класс должен перегружать, скажем, операторы сравнения и использовать таким образом:

template<typename T , typename U> 
bool f(const T& lhs , const T& rhs) 
{ 
    return wrapper(lhs) == wrapper(rhs); 
} 

Или это:

int main() 
{ 
    int a , b; 
    bool flag = wrapper(a) == wrapper(b) || wrapper(a) == wrapper(2); 
} 

Мой вопрос: Что лучший (эффективный) способ реализации такая вещь? Это вопросы, кажется широким, я имею в виду:

  • Как определить элемент value? Как T& для lvalues ​​и T для rvalues?
  • Есть ли стандартный способ создания такого универсального (rvalue и lvalue) псевдонима?
+1

Чем больше я думаю об этом, тем меньше я понимаю, чего пытается достичь такой класс-оболочка. Можете ли вы рассказать о прецеденте? –

+0

@ DanielFrey, между прочим, я думал написать набор функций сравнения с плавающей точкой, и я хотел бы использовать операторы сравнения. Мне не нравится «стиль Java» 'compare_to (a, b)', я предпочитаю перегрузку операторов. – Manu343726

+1

Интересно. В этом случае вам может понадобиться избегать неявных операторов преобразования, и только они будут помечены как «явные» или с помощью явных функций getter. И тогда вам придется перегружать операторов самостоятельно, возможно, используя [Boost.Operators] (http://www.boost.org/doc/libs/1_53_0/libs/utility/operators.htm) или мои [** df.operators **] (https://github.com/d-frey/operators/), чтобы помочь в этом. –

ответ

1

Я думаю, что Kerrek SB находится на правильном пути, предоставляя специализацию (получил мой +1 давно), поэтому каждый случай обрабатывается наиболее эффективно.

Проблема заключается в том, что вы не можете просто добавлять неявные операторы преобразования, и если вы хотите предоставить свои собственные перегрузки оператора, все может стать довольно сложным.

Решение, с которым я столкнулся, пытается справиться с этим, помещая информацию о том, какая случайная переменная находится в логическом параметре шаблона.Вот основные рамки для value_wrapper класса:

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)); 
} 

Таким образом value_wrapper «первый параметр шаблона всегда гнилой типа, теперь все упрощается:

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(); 
} 

(очевидно, вы хотите реализовать их по-разному, но вы всегда можете получить доступ к s tored значения через cref() единообразно)

Live example

Вы, возможно, потребуется изменить это, если вам нужно непостоянный доступ, и т.д., но я надеюсь, что выше поможет вам начать работу. Если вам нужна дополнительная помощь/идеи, не стесняйтесь спрашивать :)

+0

Спасибо за ваш ответ, я думаю, что это решение. Один вопрос: Считаете ли вы, что компилятор доступен для всех (или почти для большинства) машин 'value_wrapper' и генерирует только код для сравнения? Я имею в виду, если у меня есть перегрузка сравнения для 'value_wrapper' следующим образом:' bool operator == (...) {return compare (lhs.cref(), lhs.cref()); } ', и я использую его как в моих примерах (' bool flag = wrap (a) == wrap (b); '), компилятор способен генерировать код не намного сложнее, чем' bool flag = compare (a, б); – Manu343726

+0

@ Manu343726 Нет, если вы возьмете копию (возможно, переместили ее), компилятору даже не удастся удалить ее, если она не может ** доказать **, что никаких побочных эффектов (что в некоторых случаях довольно сложно) , Но также обратите внимание, что вам могут не понадобиться эти копии в любом случае, поскольку даже временные файлы уничтожаются только в конце полного выражения, в котором они встречаются. Если все использует вид 'wrap (a) == wrap (f())' it doesn ' не имеет значения, если 'f()' возвратил временный, поскольку этот временный (и ссылки на него) все еще действителен, когда вычисляется 'operator =='. –

+0

Спасибо. Я скомпилировал его с помощью GCC Explorer (GCC4.8.1), и это меня удивило, потому что даже с '-O' компилятор включал everithing (оператор value_wrapper = body включен) как для lvalues, так и для rvalues. – Manu343726

5

я бы просто обеспечить подходящие операторы преобразования:

#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) 

Конверсия операторы позволяют использовать любые операторы ed на базовом типе.

+1

Я не понимаю вторую и в этой строке 'operator T &() & noexcept {return value; } 'какую C++ информацию следует искать, с чем она связана? –

+0

@StephaneRolland: Это квалификатор lvalue. Новое в C++ 11, соединяется с последующим квалификатором rvalue. Это позволяет перегрузить ли сам объект lvalue или rvalue. –

+0

Какую проблему он защищает? что он обеспечивает? –

Смежные вопросы