Lazy initialization of std::tuple elements












0















I often use std::aligned_storage to specify an uninitialized class member. The typical example is an static_vector, which stores its elements within the structure.



However, I'm not completely sure what I should do when I want a std::tuple to be created step by step, initializing its members in an unspecified order at different points in time.



Would it be legal to create



std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>


and then reinterpret member reference as a std::tuple<Types...>&?



For example:



#include <bitset>
#include <memory>
#include <new>
#include <tuple>
#include <utility>

template < class... Ts >
class uninitialized_tuple {
public:
using tuple_type = typename std::tuple<Ts...>;
using buffer_type =
std::tuple<
typename std::aligned_storage<sizeof(Ts),alignof(Ts)>::type...
>;

~uninitialized_tuple() {
destruct_helper<std::index_sequence_for<Ts...>>::erase(*this);
}

tuple_type& as_tuple() {
reinterpret_cast<tuple_type&>(_storage);
}

bool valid() const {
return _is_set.all();
}

template < size_t index, class... Args >
void emplace( Args&&... args ) {
using element_type = typename std::tuple_element<index,tuple_type>::type;
new (&std::get<index>(_storage)) element_type( std::forward<Args>(args)...);
_is_set.set(index);
}

template < size_t index >
void erase() {
using element_type = typename std::tuple_element<index,tuple_type>::type;
if( _is_set[index] ) {
std::get<index>(_storage).~element_type();
_is_set.reset(index);
}
}

private:
template < class Seq >
struct destruct_helper {
static void erase( uninitialized_tuple& ) {}
};

template < size_t index, size_t... indices >
struct destruct_helper<std::index_sequence<index,indices...>> {
static void erase( uninitialized_tuple& value ) {
value.erase<index>();
destruct_helper<std::index_sequence<indices...>>::erase_one(value);
}
};

buffer_type _storage;
std::bitset<sizeof...(Ts)> _is_set;
};









share|improve this question


















  • 1





    I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

    – NathanOliver
    Nov 26 '18 at 21:26













  • as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

    – Peter Ruderman
    Nov 26 '18 at 21:30






  • 3





    Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

    – SergeyA
    Nov 26 '18 at 21:32






  • 2





    @NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

    – SergeyA
    Nov 26 '18 at 21:34











  • You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

    – Oliv
    Nov 26 '18 at 21:37
















0















I often use std::aligned_storage to specify an uninitialized class member. The typical example is an static_vector, which stores its elements within the structure.



However, I'm not completely sure what I should do when I want a std::tuple to be created step by step, initializing its members in an unspecified order at different points in time.



Would it be legal to create



std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>


and then reinterpret member reference as a std::tuple<Types...>&?



For example:



#include <bitset>
#include <memory>
#include <new>
#include <tuple>
#include <utility>

template < class... Ts >
class uninitialized_tuple {
public:
using tuple_type = typename std::tuple<Ts...>;
using buffer_type =
std::tuple<
typename std::aligned_storage<sizeof(Ts),alignof(Ts)>::type...
>;

~uninitialized_tuple() {
destruct_helper<std::index_sequence_for<Ts...>>::erase(*this);
}

tuple_type& as_tuple() {
reinterpret_cast<tuple_type&>(_storage);
}

bool valid() const {
return _is_set.all();
}

template < size_t index, class... Args >
void emplace( Args&&... args ) {
using element_type = typename std::tuple_element<index,tuple_type>::type;
new (&std::get<index>(_storage)) element_type( std::forward<Args>(args)...);
_is_set.set(index);
}

template < size_t index >
void erase() {
using element_type = typename std::tuple_element<index,tuple_type>::type;
if( _is_set[index] ) {
std::get<index>(_storage).~element_type();
_is_set.reset(index);
}
}

private:
template < class Seq >
struct destruct_helper {
static void erase( uninitialized_tuple& ) {}
};

template < size_t index, size_t... indices >
struct destruct_helper<std::index_sequence<index,indices...>> {
static void erase( uninitialized_tuple& value ) {
value.erase<index>();
destruct_helper<std::index_sequence<indices...>>::erase_one(value);
}
};

buffer_type _storage;
std::bitset<sizeof...(Ts)> _is_set;
};









share|improve this question


















  • 1





    I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

    – NathanOliver
    Nov 26 '18 at 21:26













  • as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

    – Peter Ruderman
    Nov 26 '18 at 21:30






  • 3





    Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

    – SergeyA
    Nov 26 '18 at 21:32






  • 2





    @NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

    – SergeyA
    Nov 26 '18 at 21:34











  • You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

    – Oliv
    Nov 26 '18 at 21:37














0












0








0








I often use std::aligned_storage to specify an uninitialized class member. The typical example is an static_vector, which stores its elements within the structure.



However, I'm not completely sure what I should do when I want a std::tuple to be created step by step, initializing its members in an unspecified order at different points in time.



Would it be legal to create



std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>


and then reinterpret member reference as a std::tuple<Types...>&?



For example:



#include <bitset>
#include <memory>
#include <new>
#include <tuple>
#include <utility>

template < class... Ts >
class uninitialized_tuple {
public:
using tuple_type = typename std::tuple<Ts...>;
using buffer_type =
std::tuple<
typename std::aligned_storage<sizeof(Ts),alignof(Ts)>::type...
>;

~uninitialized_tuple() {
destruct_helper<std::index_sequence_for<Ts...>>::erase(*this);
}

tuple_type& as_tuple() {
reinterpret_cast<tuple_type&>(_storage);
}

bool valid() const {
return _is_set.all();
}

template < size_t index, class... Args >
void emplace( Args&&... args ) {
using element_type = typename std::tuple_element<index,tuple_type>::type;
new (&std::get<index>(_storage)) element_type( std::forward<Args>(args)...);
_is_set.set(index);
}

template < size_t index >
void erase() {
using element_type = typename std::tuple_element<index,tuple_type>::type;
if( _is_set[index] ) {
std::get<index>(_storage).~element_type();
_is_set.reset(index);
}
}

private:
template < class Seq >
struct destruct_helper {
static void erase( uninitialized_tuple& ) {}
};

template < size_t index, size_t... indices >
struct destruct_helper<std::index_sequence<index,indices...>> {
static void erase( uninitialized_tuple& value ) {
value.erase<index>();
destruct_helper<std::index_sequence<indices...>>::erase_one(value);
}
};

buffer_type _storage;
std::bitset<sizeof...(Ts)> _is_set;
};









share|improve this question














I often use std::aligned_storage to specify an uninitialized class member. The typical example is an static_vector, which stores its elements within the structure.



However, I'm not completely sure what I should do when I want a std::tuple to be created step by step, initializing its members in an unspecified order at different points in time.



Would it be legal to create



std::tuple< std::aligned_storage<sizeof(Types),alignof(Types)>::type...>


and then reinterpret member reference as a std::tuple<Types...>&?



For example:



#include <bitset>
#include <memory>
#include <new>
#include <tuple>
#include <utility>

template < class... Ts >
class uninitialized_tuple {
public:
using tuple_type = typename std::tuple<Ts...>;
using buffer_type =
std::tuple<
typename std::aligned_storage<sizeof(Ts),alignof(Ts)>::type...
>;

~uninitialized_tuple() {
destruct_helper<std::index_sequence_for<Ts...>>::erase(*this);
}

tuple_type& as_tuple() {
reinterpret_cast<tuple_type&>(_storage);
}

bool valid() const {
return _is_set.all();
}

template < size_t index, class... Args >
void emplace( Args&&... args ) {
using element_type = typename std::tuple_element<index,tuple_type>::type;
new (&std::get<index>(_storage)) element_type( std::forward<Args>(args)...);
_is_set.set(index);
}

template < size_t index >
void erase() {
using element_type = typename std::tuple_element<index,tuple_type>::type;
if( _is_set[index] ) {
std::get<index>(_storage).~element_type();
_is_set.reset(index);
}
}

private:
template < class Seq >
struct destruct_helper {
static void erase( uninitialized_tuple& ) {}
};

template < size_t index, size_t... indices >
struct destruct_helper<std::index_sequence<index,indices...>> {
static void erase( uninitialized_tuple& value ) {
value.erase<index>();
destruct_helper<std::index_sequence<indices...>>::erase_one(value);
}
};

buffer_type _storage;
std::bitset<sizeof...(Ts)> _is_set;
};






c++ c++14 lazy-initialization stdtuple






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 26 '18 at 21:15









Jorge BellónJorge Bellón

1,136719




1,136719








  • 1





    I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

    – NathanOliver
    Nov 26 '18 at 21:26













  • as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

    – Peter Ruderman
    Nov 26 '18 at 21:30






  • 3





    Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

    – SergeyA
    Nov 26 '18 at 21:32






  • 2





    @NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

    – SergeyA
    Nov 26 '18 at 21:34











  • You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

    – Oliv
    Nov 26 '18 at 21:37














  • 1





    I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

    – NathanOliver
    Nov 26 '18 at 21:26













  • as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

    – Peter Ruderman
    Nov 26 '18 at 21:30






  • 3





    Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

    – SergeyA
    Nov 26 '18 at 21:32






  • 2





    @NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

    – SergeyA
    Nov 26 '18 at 21:34











  • You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

    – Oliv
    Nov 26 '18 at 21:37








1




1





I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

– NathanOliver
Nov 26 '18 at 21:26







I don't think it would be legal. AFAIK the size of the types std::aligned_storage<sizeof(Types),alignof(Types)>::type... can be larger than Types..., which would then give you unaligned access.

– NathanOliver
Nov 26 '18 at 21:26















as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

– Peter Ruderman
Nov 26 '18 at 21:30





as_tuple looks like undefined behaviour to me. You might want to tag this one as language-lawyer.

– Peter Ruderman
Nov 26 '18 at 21:30




3




3





Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

– SergeyA
Nov 26 '18 at 21:32





Certainly not valid. You can't alias std::tuple<X> with std::tuple<Y>.

– SergeyA
Nov 26 '18 at 21:32




2




2





@NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

– SergeyA
Nov 26 '18 at 21:34





@NathanOliver they are just completely unrelated types, so first off, it is a strict aliasing violation. But even disregarding that, there is nothing at all anywhere in the standard that std::tuple<X> might have any compatible layout with std::tuple<Z>.

– SergeyA
Nov 26 '18 at 21:34













You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

– Oliv
Nov 26 '18 at 21:37





You can not access an object that has never been created. Creating the element is not sufficient. Considering your code you do not need much help. You should better wrap each elements in a union unitialized{ element_type value; char c='';uninitialized()=default;....~unitialized(){}} and make you unintialized_tuple a... tuple => implement get, and specialize tuple_size and tuple_element.

– Oliv
Nov 26 '18 at 21:37












1 Answer
1






active

oldest

votes


















2














Accessing whatever is returned by as_tuple() is undefined behavior as it breaks the type aliasing rules. Please refer to https://en.cppreference.com/w/cpp/language/reinterpret_cast:




Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:




  • AliasedType and DynamicType are similar.


  • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.


  • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.








share|improve this answer























    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53489174%2flazy-initialization-of-stdtuple-elements%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    Accessing whatever is returned by as_tuple() is undefined behavior as it breaks the type aliasing rules. Please refer to https://en.cppreference.com/w/cpp/language/reinterpret_cast:




    Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:




    • AliasedType and DynamicType are similar.


    • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.


    • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.








    share|improve this answer




























      2














      Accessing whatever is returned by as_tuple() is undefined behavior as it breaks the type aliasing rules. Please refer to https://en.cppreference.com/w/cpp/language/reinterpret_cast:




      Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:




      • AliasedType and DynamicType are similar.


      • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.


      • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.








      share|improve this answer


























        2












        2








        2







        Accessing whatever is returned by as_tuple() is undefined behavior as it breaks the type aliasing rules. Please refer to https://en.cppreference.com/w/cpp/language/reinterpret_cast:




        Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:




        • AliasedType and DynamicType are similar.


        • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.


        • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.








        share|improve this answer













        Accessing whatever is returned by as_tuple() is undefined behavior as it breaks the type aliasing rules. Please refer to https://en.cppreference.com/w/cpp/language/reinterpret_cast:




        Whenever an attempt is made to read or modify the stored value of an object of type DynamicType through a glvalue of type AliasedType, the behavior is undefined unless one of the following is true:




        • AliasedType and DynamicType are similar.


        • AliasedType is the (possibly cv-qualified) signed or unsigned variant of DynamicType.


        • AliasedType is std::byte, (since C++17)char, or unsigned char: this permits examination of the object representation of any object as an array of bytes.









        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 26 '18 at 21:53









        Vittorio RomeoVittorio Romeo

        58.1k17158300




        58.1k17158300
































            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53489174%2flazy-initialization-of-stdtuple-elements%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            A CLEAN and SIMPLE way to add appendices to Table of Contents and bookmarks

            Calculate evaluation metrics using cross_val_predict sklearn

            Insert data from modal to MySQL (multiple modal on website)