2015-06-09 2 views
9

Я не понимаю, почему нет optional tuple, и я имею в виду, что-то вроде этого; optional<int,string,char>, который объединил бы optional int, optional string и optional char.Создайте дополнительный набор

Вы можете думать о нем, как оптимизированной std::tuple<boost::optional<T>...>

где booleans используется optionals бы все вместе в конце structure для того, чтобы упаковать его, или даже лучше, хранить в BitSet.

Это может уменьшить память структуры много, а также более элегантна:

std::tuple<boost::optional<int>,boost::optional<string>,boost::optional<char>>

VS

optional<int,string,char>

Я могу придумать способ сделать это, используя реализацию boost::optional и variadic templates, но прежде чем начать это, я хотел бы знать, если это ag ood идея, что было бы лучшим способом реализации этого, каковы трудности, с которыми мне пришлось столкнуться?

EDIT:

В принципе, почему мне не нравится std::tuple<boost::optional<T>...>;

Так как optional<T> является объединением T и bool:

enter image description here

Новая структура может сохранить много памяти!

+0

Я знаю, что вы говорите об ускорении, но все же разрабатываются типы словаря C++ 17 «словарь». Вы должны отправить предложение в комитет C++, если вы думаете, что std :: optional может использовать этот код – KABoissonneault

+0

. Кроме того, возможно, вы могли бы определить свой собственный «необязательный» псевдоним шаблона вместе с заводской функцией для объявления и создания вашего варианта с опцией. – KABoissonneault

+0

@ KABoissonneault Я думал о чем-то подобном, но я не уверен, что не будет никаких проблем с «необязательными» данными о движении. –

ответ

1

Вы можете реализовать себя, что-то похожее на:

/** 
* Similar to `optional`, but take the bool as argument. 
*/ 
template <typename T> 
class out_optional 
{ 
public: 
    out_optional() {} 

    out_optional(const out_optional&) = delete; 
    out_optional& operator =(const out_optional&) = delete; 

    void destruct(bool b) { if (b) { reset(b); } } 

    void reset(bool& b) { if (b) { reinterpret_cast<T*>(data)->~T(); b = false; } } 
    void reset(bool& b, const T& value) { reset(b); new (data) T{value}; b = true; } 
    void reset(bool& b, T&& value) { reset(b); new (data) T{value}; b = true; } 

    const T* get_ptr(bool b) const { return b ? reinterpret_cast<T*>(data) : nullptr; } 
    T* get_ptr(bool b) { return b ? reinterpret_cast<T*>(data) : nullptr; } 

    const T& get(bool b) const { assert(b); return *get_ptr(b); } 
    T& get(bool b) { assert(b); return *get_ptr(b); } 

    // Other stuff as swap, pseudo assignment/move, more constructors 

private: 
    alignas(T) char data[sizeof(T)]; 
}; 

/** 
* 'Tuple' of optional, packaged with bool at the end. 
*/ 
template <typename ... Ts> 
struct multi_optional 
{ 
    template <std::size_t I> 
    using type = typename std::tuple_element<I, std::tuple<Ts...>>::type; 

    static_assert(std::is_same<int, type<0>>::value, ""); 
public: 
    multi_optional() = default; 

    ~multi_optional() 
    { 
     destruct(std::index_sequence_for<Ts...>()); 
    } 

    multi_optional(const multi_optional&) = delete; // To be implemented. 
    multi_optional& operator =(const multi_optional&) = delete; // To be implemented. 

    template <std::size_t I> 
    const auto* get_ptr() const { return std::get<I>(data).get_ptr(flags[I]); } 

    template <std::size_t I> 
    auto* get_ptr() { return std::get<I>(data).get_ptr(flags[I]); } 

    template <std::size_t I> 
    const auto& get() const { return std::get<I>(data).get(flags[I]); } 

    template <std::size_t I> 
    auto& get() { return std::get<I>(data).get(flags[I]); } 

    template <std::size_t I> 
    void reset() { return std::get<I>(data).reset(flags[I]); } 

    template <std::size_t I> 
    void reset(const type<I>& value) { return std::get<I>(data).reset(flags[I], value); } 


    // Other stuff as copy/move assignment/constructor, ... 

private: 
    template <std::size_t ... Is> 
    void destruct(std::index_sequence<Is...>) 
    { 
     int dummy[] = { 0, (std::get<Is>(data).destruct(flags[Is]), 0)... }; 
     static_cast<void>(dummy); 
    } 

private: 
    std::tuple<out_optional<Ts>...> data; 
    std::array<bool, sizeof...(Ts)> flags = {{}}; 
}; 

Live Demo

+0

Спасибо за реализацию, это хорошая работа. Но я все еще хочу знать, будет ли использование этого более эффективным, чем 'std :: tuple ...>;' ?? (независимо от полученной памяти) –

+0

@ OthmanBenchekroun: Как вы объясните в своей схеме, вы получаете некоторое пространство для заполнения (в этом отношении также важна Order for Ts). Вы также можете заменить мой 'array ' более компактной структурой ('std :: bitset'). Для эффективности, поскольку 'bool' больше не находится рядом с его' data', он менее кэширован (теперь он зависит также от количества типов в 'multi_optional'). – Jarod42

+0

ok Я вижу, я попытаюсь выполнить некоторые тесты, используя вашу реализацию. –

0

Нельзя ли использовать псевдоним шаблона?

template <typename... T> using optuple = 
    boost::optional<std::tuple<T...> >; 

Или, фактически, другой вариант, если вы предпочитаете;

template <typename... T> using optuple = 
    std::tuple<boost::optional<T>...>; 

Теперь вы можете просто написать

optuple<std::string, double, int> x; 

и получить значение вы выбрали.

Конечно, если только один из членов кортежа всегда заполнены, уже существует: boost::variant<T...>

+1

@KABoissonneault И вы перестали читать раньше, кажется – sehe

+0

Хорошо, это было отредактировано после – KABoissonneault

+0

Спасибо за ответ, однако, как я объяснил, я действительно не хочу использовать 'std :: tuple ... >; 'Структура имеет много дополнений из-за' boolean' между данными –

0

Структура вы можете уже создать как специализацию tuple<T>. Нет смысла вводить дополнительную структуру. Здесь нет ничего особенного, кроме специального vector, чтобы использовать EBO.

То, что вы хотите, прекрасно использует существующие интерфейсы. Если вы захотите выполнить , вы всегда можете специализироваться, я думаю, или скачу свое собственное.

+0

Я думаю (предполагается, что 'std :: optional' уже существует), который частично специализируется на' template class std :: tuple ...> 'будет незаконным. По обычным правилам, что специализации в пространстве имен std должны быть для пользовательских типов? – sehe

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