Lazy initialization of std::tuple elements
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
|
show 2 more comments
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
1
I don't think it would be legal. AFAIK the size of the typesstd::aligned_storage<sizeof(Types),alignof(Types)>::type...
can be larger thanTypes...
, 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 aslanguage-lawyer
.
– Peter Ruderman
Nov 26 '18 at 21:30
3
Certainly not valid. You can't aliasstd::tuple<X>
withstd::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 thatstd::tuple<X>
might have any compatible layout withstd::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 aunion 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
|
show 2 more comments
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
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
c++ c++14 lazy-initialization stdtuple
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 typesstd::aligned_storage<sizeof(Types),alignof(Types)>::type...
can be larger thanTypes...
, 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 aslanguage-lawyer
.
– Peter Ruderman
Nov 26 '18 at 21:30
3
Certainly not valid. You can't aliasstd::tuple<X>
withstd::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 thatstd::tuple<X>
might have any compatible layout withstd::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 aunion 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
|
show 2 more comments
1
I don't think it would be legal. AFAIK the size of the typesstd::aligned_storage<sizeof(Types),alignof(Types)>::type...
can be larger thanTypes...
, 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 aslanguage-lawyer
.
– Peter Ruderman
Nov 26 '18 at 21:30
3
Certainly not valid. You can't aliasstd::tuple<X>
withstd::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 thatstd::tuple<X>
might have any compatible layout withstd::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 aunion 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
|
show 2 more comments
1 Answer
1
active
oldest
votes
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.
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
add a comment |
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.
add a comment |
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.
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.
answered Nov 26 '18 at 21:53
Vittorio RomeoVittorio Romeo
58.1k17158300
58.1k17158300
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
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 thanTypes...
, 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 aslanguage-lawyer
.– Peter Ruderman
Nov 26 '18 at 21:30
3
Certainly not valid. You can't alias
std::tuple<X>
withstd::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 withstd::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