Reputation: 61
I have a class inherited from standard tuple:
#include <concepts>
#include <cstdint>
#include <iostream>
#include <string>
#include <tuple>
#include <vector>
template< typename ... Args >
class Identifier: public std::tuple< Args... >
{
public:
Identifier( Args... args ) requires( sizeof...( Args ) != 0 ) : std::tuple< Args... >( std::move( args )... )
{}
Identifier( std::tuple< Args... > tuple ):std::tuple< Args... >( tuple )
{}
Identifier()
{}
template< typename ... T >
requires std::convertible_to< std::tuple< Args... >, std::tuple< T... > >
Identifier( const Identifier< T... >& identifier ): std::tuple< Args... >(static_cast< std::tuple< T... > >(identifier))
{}
};
int main()
{
Identifier<std::wstring, std::int64_t> item(L"id", 10);
Identifier<const std::wstring&, std::int64_t> item_ref = item;
std::vector< Identifier< const std::wstring&, std::int64_t > > collection;
collection.emplace_back( item );
}
At this point:
Identifier<const std::wstring&, int64_t> item_ref = item;
item_ref
has correct value.
Then I put item into a vector:
std::vector< Identifier< const std::wstring&, std::int64_t > > collection;
collection.emplace_back( item );
I got invalid value in string reference in element in vector and invalid value in string reference in item_ref.
Why did that happen, and how do I fix it?
Upvotes: -1
Views: 135
Reputation: 1678
You firstly use the Identifier(Args...)
constructor, then you use the other one twice.
template< typename ... T >
requires std::convertible_to< std::tuple< Args... >, std::tuple< T... > >
Identifier( const Identifier< T... >& identifier ): std::tuple< Args... >(static_cast< std::tuple< T... > >(identifier))
{}
When item
is passed to it, firstly a const Identifier<T...>&
to it is created as argument to the constructor. Then this reference is static_cast
ed to a std::tuple<T...>
.
Note that T...
is std::wstring, int64_t
, as this is item
s bases templated types.
This means that you cast away the reference, resulting in a temporary copy of the original item
. Then you assign that copy's std::wstring
member to a const std::wstring&
via the construction of the base. After the construction, the temporary gets destroyed, leaving the object referencing an already deleted std::wstring
.
To solve your problem you could e.g. just drop the static_cast
and pass the argument as is.
As @TedLyngmo mentioned here, you most likely have the order of the std::convertible_to
reversed.
It should be
// std::convertible_to<From, To>;
std::convertible_to<std::tuple<T... >, std::tuple<Args...>>
Which could be further modified to the following:
Identifier(std::convertible_to<std::tuple<Args...>> auto&& identifier)
: std::tuple<Args...>(std::forward<decltype(identifier)>(identifier)) {}
This uses std::forward
on the forwarding reference to perfectly forward any arguments you pass to it. You could apply this to the other constructors as well.
I got invalid value in string reference in element in vector and invalid value in string reference in item_ref.
How do you know? Did you print the values to std::wcout
? Well, at least I tried to do this and (depending on how I compiled it) I got either garbage output or even a crash.
I suggest you enable all warnings (I still got none) and enable other tools like -fsanitize=undefined
which resulted in the following:
UndefinedBehaviorSanitizer:DEADLYSIGNAL
==1==ERROR: UndefinedBehaviorSanitizer: stack-overflow on address 0x7ffd94afc000 (pc 0x7ee02548a31e bp 0x59c5bf4e1e6b sp 0x7ffd94afb190 T1)
#0 0x7ee02548a31e in __gnu_cxx::stdio_sync_filebuf<wchar_t, std::char_traits<wchar_t>>::xsputn(wchar_t const*, long) (/opt/compiler-explorer/gcc-14.2.0/lib64/libstdc++.so.6+0x12331e) (BuildId: 998334304023149e8c44e633d4a2c69800a2eb79)
#1 0x7ee0254b65e4 in std::basic_ostream<wchar_t, std::char_traits<wchar_t>>& std::__ostream_insert<wchar_t, std::char_traits<wchar_t>>(std::basic_ostream<wchar_t, std::char_traits<wchar_t>>&, wchar_t const*, long) (/opt/compiler-explorer/gcc-14.2.0/lib64/libstdc++.so.6+0x14f5e4) (BuildId: 998334304023149e8c44e633d4a2c69800a2eb79)
#2 0x59c5bf4e11c7 in main /app/example.cpp:35:15
#3 0x7ee025029d8f (/lib/x86_64-linux-gnu/libc.so.6+0x29d8f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
#4 0x7ee025029e3f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x29e3f) (BuildId: 490fef8403240c91833978d494d39e537409b92e)
#5 0x59c5bf4b4494 in _start (/app/output.s+0xa494)
SUMMARY: UndefinedBehaviorSanitizer: stack-overflow (/opt/compiler-explorer/gcc-14.2.0/lib64/libstdc++.so.6+0x12331e) (BuildId: 998334304023149e8c44e633d4a2c69800a2eb79) in __gnu_cxx::stdio_sync_filebuf<wchar_t, std::char_traits<wchar_t>>::xsputn(wchar_t const*, long)
==1==ABORTING
id
https://godbolt.org/z/5eja84EaM
Upvotes: 3