I am in a scenario where I need to process a variable-sized array of constant-sized C-strings, namely: I want to know if each of those strings is unique.
I tried using a std::set
, as uniqueness of its contents is a given (note: I used a single const-sized array here, as errors produced are the same):
#include <set>
int main(){
std::set<wchar_t[4]> sSet;
const wchar_t[4] text = L":)\0";
// checking if successful
// set.insert() returns a pair of <iterator, bool>
// when used like this
std::pair<std::set<wchar_t[4]>::iterator, bool> result = \
sSet.insert(text);
if(!result.second) std::cout << "Value not unique!";
return 0;
}
The mentioned code crashes in a spectacular manner:
/usr/include/c++/11/bits/alloc_traits.h:518:28: error: no matching function for call to ‘construct_at(wchar_t (*&)[4], const wchar_t [4])’
518 | std::construct_at(__p, std::forward<_Args>(__args)...);
| ~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
(note: I skipped a lot of stack tracing here)
/usr/include/c++/11/bits/stl_construct.h:96:56: error: invalid conversion from ‘const wchar_t*’ to ‘wchar_t’ [-fpermissive]
96 | -> decltype(::new((void*)0) _Tp(std::declval<_Args>()...))
| ~~~~~~~~~~~~~~~~~~~^~
| |
| const wchar_t*
(a dozen or so notes follow,
some of them are about passing a wrong number of parameters,
and most are beyond me, see next line)
/usr/include/c++/11/bits/stl_set.h:518:7: note: candidate: ‘std::pair, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator, bool> std::set<_Key, _Compare, _Alloc>::insert(std::set<_Key, _Compare, _Alloc>::value_type&&) [with _Key = wchar_t [4]; _Compare = std::less; _Alloc = std::allocator; typename std::_Rb_tree<_Key, _Key, std::_Identity<_Tp>, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other>::const_iterator = std::_Rb_tree, std::less, std::allocator >::const_iterator; typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key>::other = std::allocator; typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<_Key> = __gnu_cxx::__alloc_traits, wchar_t [4]>::rebind; typename _Alloc::value_type = wchar_t [4]; std::set<_Key, _Compare, _Alloc>::value_type = wchar_t [4]]’
518 | insert(value_type&& __x)
| ^~~~~~
Compiling with MSVC yielded something entirely different:
error C3074: an array can only be initialized with an initializer-list
(curly bracing, as per error's suggestion, did not work)
As it seems to be an error inside of stdlibs' files, I am curious: what happened here? Approach I've shown works perfectly with sets of types which are not arrays, like std::set<int>
.
I suspect that I violated the intended syntax, which is insert(value_type&& value);
, as per a reference (note: my target standard is C++11). If so, what have I done incorrectly? I find the error messages inconclusive, and toying around with std
's files is yet beyond me.
I know my initial problem can be solved in various ways, I'm using a vector of vectors for now - I'm still intrigued by this std::set
behaviour, though.
Guess this has been more or less answered in the comments, but I think it might be worth pulling it all together to help you on your way.
When declaring a set
(and other STL containers), the type of the elements in a set needs to be a 'value type', and a C-style array ain't. But std::array
is, so, as Ted says, use that instead.
Knowing that, the changes you need to make should be obvious, but, being the pedant that I am, I've made them anyway:
#include <iostream>
#include <set>
#include <utility>
#include <array>
using wc4 = std::array <wchar_t, 4>;
int main ()
{
std::set <wc4> sSet;
wc4 text = { L":)\0" };
auto result = sSet.insert (text);
if (!result.second)
std::cout << "Value not unique!";
}
Notes:
auto
, when you can.\0
in there. Now you have two.#includes
. It will stand you in good stead later on in your career, if nothing else. utility
was missing here (for std::pair
).