#include <iostream>
#include <unordered_map>
#include <string>
struct tree_node {
// tree_node() : attrib_val{"null"} {}
std::unordered_map<std::string, tree_node> child;
};
int main(int argc, char const *argv[])
{
return 0;
}
This code compiles just fine on my mac with clang:
$ g++ --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
$ g++ -std=c++11 test.cpp
$
On my linux machine, with gcc 9.1.0, I get the following error:
In file included from /usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_algobase.h:64,
from /usr/um/gcc-9.1.0/include/c++/9.1.0/bits/char_traits.h:39,
from /usr/um/gcc-9.1.0/include/c++/9.1.0/ios:40,
from /usr/um/gcc-9.1.0/include/c++/9.1.0/ostream:38,
from /usr/um/gcc-9.1.0/include/c++/9.1.0/iostream:39,
from test.cpp:1:
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_pair.h: In instantiation of ‘struct std::pair<const std::__cxx11::basic_string<char>, tree_node>’:
/usr/um/gcc-9.1.0/include/c++/9.1.0/ext/aligned_buffer.h:91:28: required from ‘struct __gnu_cxx::__aligned_buffer<std::pair<const std::__cxx11::basic_string<char>, tree_node> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:233:43: required from ‘struct std::__detail::_Hash_node_value_base<std::pair<const std::__cxx11::basic_string<char>, tree_node> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:264:12: required from ‘struct std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, tree_node>, true>’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable_policy.h:2016:13: required from ‘struct std::__detail::_Hashtable_alloc<std::allocator<std::__detail::_Hash_node<std::pair<const std::__cxx11::basic_string<char>, tree_node>, true> > >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/hashtable.h:173:11: required from ‘class std::_Hashtable<std::__cxx11::basic_string<char>, std::pair<const std::__cxx11::basic_string<char>, tree_node>, std::allocator<std::pair<const std::__cxx11::basic_string<char>, tree_node> >, std::__detail::_Select1st, std::equal_to<std::__cxx11::basic_string<char> >, std::hash<std::__cxx11::basic_string<char> >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >’
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/unordered_map.h:105:18: required from ‘class std::unordered_map<std::__cxx11::basic_string<char>, tree_node>’
test.cpp:7:46: required from here
/usr/um/gcc-9.1.0/include/c++/9.1.0/bits/stl_pair.h:215:11: error: ‘std::pair<_T1, _T2>::second’ has incomplete type
215 | _T2 second; /// #c second is a copy of the second object
| ^~~~~~
test.cpp:5:8: note: forward declaration of ‘struct tree_node’
5 | struct tree_node {
It doesn't like the tree_node as a value in unordered_map for some reason.
This is undefined behavior, by [res.on.functions]/2.5:
[The effects are undefined if] an incomplete type ([basic.types]) is used as a template argument when instantiating a template component or evaluating a concept, unless specifically allowed for that component.
This is an annoying case where I basically have to prove a negative for this answer to be valid, but I find no place in the standard that mentions an exception that allows you to use an incomplete type in a std::map. Therefore, this program can do anything. In particular, though Clang compiles it now, it may stop working at any point in the future, and there's also a chance that the compiled code map specialization doesn't work properly. Certain containers, especially std::vector, have a clause that allows them to be instantiated at incomplete types under the right conditions. But this case is undefined behavior, and so compilers have no obligation to warn you or error. Change your program somehow to avoid this. I believe the following would be legal, without forcing you to store too many extra pointers.
struct tree_node {
std::unique_ptr<std::unordered_map<std::string, tree_node>> child;
};
std::unique_ptr is an exception to the general no-go rule—it's OK to instantiate it at an incomplete type (but some of its members aren't as lax). I believe that this means that std::unordered_map<std::string, tree_node> is not required to be complete at the point in the definition of tree_node where the specialization of std::unique_ptr is required to be complete, and so the std::unordered_map specialization is not triggered and UB is avoided since tree_node is not required to be complete. Note that you can still write constructors, functions, a destructor, etc. without worry, since all of those definitions are implicitly moved out of and after the class definition, and tree_node becomes complete after the class definition ends.
Changing:
struct tree_node {
// tree_node() : attrib_val{"null"} {}
std::unordered_map<std::string, tree_node> child;
};
To:
struct tree_node {
// tree_node() : attrib_val{"null"} {}
std::unordered_map<std::string, tree_node *> child;
};
Makes the compilation problem go away. Whether it has any reflection on what you wanted?
Related
I have an atomic wrapping a chrono time_point value. The default construction of time_point is fine for me, so I expect to not need to explicitly set it. However in gcc I get a compiler error without explicitly setting the default. Here's a minimal example:
#include <atomic>
#include <chrono>
struct A
{
using TimePoint = std::chrono::system_clock::time_point;
std::atomic<TimePoint> point;
};
int
main()
{
A a;
return 0;
}
Here's the error message:
<source>: In function 'int main()':
<source>:13:7: error: use of deleted function 'A::A()'
A a;
^
<source>:6:5: note: 'A::A()' is implicitly deleted because the default definition would be ill-formed:
A() = default;
^
<source>:6:5: error: use of deleted function 'constexpr std::atomic<_Tp>::atomic() [with _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >]'
In file included from <source>:1:
/opt/compiler-explorer/gcc-8.3.0/include/c++/8.3.0/atomic:194:7: note: 'constexpr std::atomic<_Tp>::atomic() noexcept [with _Tp = std::chrono::time_point<std::chrono::_V2::system_clock, std::chrono::duration<long int, std::ratio<1, 1000000000> > >]' is implicitly deleted because its exception-specification does not match the implicit exception-specification ''
atomic() noexcept = default;
^~~~~~
Compiler returned: 1
Here's a godbolt link:
https://godbolt.org/z/YocxGd
I can work around this by simply explicitly adding a default initialization (https://godbolt.org/z/8z5WqW):
std::atomic<TimePoint> point{TimePoint{}};
But it seems silly that I would need to do that. I don't understand what is wrong. I notice that clang and gcc starting with 10.x don't complain about the implicit default. Is this just a compiler bug with the older versions of gcc? Or is there a more elegant way I should handle this than the explicit default initialization of time_point?
Note that std::atomic<std::chrono::high_resolution_clock::time_point> can not compile references this same error message but is asking about (and gets answers for) the mechanics of sharing the time_point between threads. I'm not having a problem with that. I'm asking particularly about why the implicit default constructed value isn't working when the explicit default constructed value does work.
Nice answer #Nicol - but I'm going to go with a libstdc++ bug.
The following code:
#include <atomic>
struct A {
A() {}
};
int main () {
std::atomic<A> a;
}
gives a similar error on gcc 8.3/9.x, but compiles w/o error on gcc 10.x (all with -std=c++17)
clang8 + libstdc++ 8.3 fails as well.
clang + libc++ compile w/o error.
The code
#include <list>
#include <memory>
class B;
class A {
std::list<std::unique_ptr<B>> bs;
public:
A();
~A();
};
int main()
{
A x;
return 0;
}
obviously compiles. It doesn't link because A::A() and A::~A() are missing, but that is expected and alright. Changing
std::list<std::unique_ptr<B>> bs;
which is supposed to call the std::list's standard constructor
list() : list(Allocator()) {}
(C++14 and up) to
std::list<std::unique_ptr<B>> bs{};
which is supposed to call list(std::initializer_list, const Allocator & = Allocator());
the default constructor too.
(Thanks to Nicol Bolas, who rightly referred to [over.match.list] 13.3.1.7)
gives the following error with c++ (Ubuntu 5.2.1-22ubuntu2) 5.2.1 20151010 and the --std=c++17 parameter:
/usr/include/c++/5/bits/unique_ptr.h: In instantiation of ‘void std::default_delete<_Tp>::operator()(_Tp*) const [with _Tp = B]’:
/usr/include/c++/5/bits/unique_ptr.h:236:17: required from ‘std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = B; _Dp = std::default_delete<B>]’
/usr/include/c++/5/bits/stl_list.h:106:12: required from ‘void __gnu_cxx::new_allocator<_Tp>::destroy(_Up*) [with _Up = std::_List_node<std::unique_ptr<B> >; _Tp = std::_List_node<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/list.tcc:75:4: required from ‘void std::__cxx11::_List_base<_Tp, _Alloc>::_M_clear() [with _Tp = std::unique_ptr<B>; _Alloc = std::allocator<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/stl_list.h:446:17: required from ‘std::__cxx11::_List_base<_Tp, _Alloc>::~_List_base() [with _Tp = std::unique_ptr<B>; _Alloc = std::allocator<std::unique_ptr<B> >]’
/usr/include/c++/5/bits/stl_list.h:507:11: required from here
/usr/include/c++/5/bits/unique_ptr.h:74:22: error: invalid application of ‘sizeof’ to incomplete type ‘B’
static_assert(sizeof(_Tp)>0,
^
Which barks about the type of B being incomplete. My question is:
Why does the initializer_list constructor need the complete type of B for an empty initializer list?
Pointers to the relevant parts of the standard are always appreciated.
I think you've stepped onto the bleeding edge.
This appears to be an active issue on the CWG (Core Working Group on the C++ committee).
CWG 1396 appears to be concerned with this very issue. This issue links to CWG 1360 which says in part:
The problem is exacerbated with class templates, since the current
direction of CWG is to instantiate member initializers only when they
are needed (see issue 1396).
In your example, the initializer of bs is never needed, and thus by the "direction" referred to above, should never be instantiated. We just aren't there yet today. Both of these issues have status drafting, meaning: they're working on it.
FWIW, VS-2015 as reported at http://webcompiler.cloudapp.net compiles (but of course does not link) this example.
i have to continue a programm. The programmer before me used the structure a lot:
std:vector< T* const>
He wrote ist in Visual Studio C++ 2010 and was able to compile this. I am using g++ and it thwrows some compilation errors.
g++ -g -Wall -c -std=c++11 -pedantic -I/usr/include/SuperLU/ src/Cell.cpp -o obj/Cell.o
In file included from src/Cell.cpp:13:0:
src/Cell.h:81:2: warning: extra ';' [-pedantic]
In file included from /usr/include/x86_64-linux-gnu/c++/4.7/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from /usr/include/c++/4.7/bits/locale_classes.h:42,
from /usr/include/c++/4.7/bits/ios_base.h:43,
from /usr/include/c++/4.7/ios:43,
from /usr/include/c++/4.7/ostream:40,
from /usr/include/c++/4.7/iostream:40,
from src/Cell.cpp:2:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of 'struct __gnu_cxx::new_allocator<BattPackage::Leg* const>':
/usr/include/c++/4.7/bits/allocator.h:89:11: required from 'class std::allocator<BattPackage::Leg* const>'
/usr/include/c++/4.7/bits/alloc_traits.h:92:43: required from 'struct std::allocator_traits<std::allocator<BattPackage::Leg* const> >'
/usr/include/c++/4.7/ext/alloc_traits.h:110:10: required from 'struct __gnu_cxx::__alloc_traits<std::allocator<BattPackage::Leg* const> >'
/usr/include/c++/4.7/bits/stl_vector.h:76:28: required from 'struct std::_Vector_base<BattPackage::Leg* const, std::allocator<BattPackage::Leg* const> >'
/usr/include/c++/4.7/bits/stl_vector.h:208:11: required from 'class std::vector<BattPackage::Leg* const>'
src/Cell.h:283:39: required from here
/usr/include/c++/4.7/ext/new_allocator.h:83:7: error: 'const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = BattPackage::Leg* const; __gnu_cxx::new_allocator<_Tp>::const_pointer = BattPackage::Leg* const*; __gnu_cxx::new_allocator<_Tp>::const_reference = BattPackage::Leg* const&]' cannot be overloaded
/usr/include/c++/4.7/ext/new_allocator.h:79:7: error: with '_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = BattPackage::Leg* const; __gnu_cxx::new_allocator<_Tp>::pointer = BattPackage::Leg* const*; __gnu_cxx::new_allocator<_Tp>::reference = BattPackage::Leg* const&]
What he wanted with this structure was a vector of pointers, where he can add and remove the pointers and manipulate the target of the pointers but not change on which object the pointer points.
As far as I understand it should not compile because T* const is not assignable.
Does anyone know why it compiles in Visual Studio ?
And can I replace that in the declarations without the need to change the complete code?
std::vector uses an Allocator to allocate, reallocate, and deallocate memory for its members. The Allocator requirements are only defined for an Allocator X for type T, where T is "any non-const, non-reference object type" (C++11 Table 27). So using a std::vector<T* const> gives you undefined behaviour, because the allocator it uses is not defined for a const element type.
You're better off fixing this as soon as possible. There are unlikely to be many issues with changing the declarations to std::vector<T*>.
I have the following code (as much simplified as possible):
#include <vector>
#include <algorithm>
using std::vector;
enum class Foo {
BAR, BAZ
};
void print_to_file(const vector<const vector<Foo> >& sequences) {
std::for_each(sequences.begin(), sequences.end(), [](const vector<Foo>& sequence) {
});
}
int main(int argc, char **argv) {
return EXIT_SUCCESS;
}
when compiling with g++ (SUSE Linux) 4.7.1 20120723 [gcc-4_7-branch revision 189773] as g++ --std=c++11 test.cpp I get the following error message:
In file included from /usr/include/c++/4.7/x86_64-suse-linux/bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/vector:62,
from test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘struct __gnu_cxx::new_allocator<const std::vector<Foo> >’:
/usr/include/c++/4.7/bits/allocator.h:89:11: required from ‘class std::allocator<const std::vector<Foo> >’
/usr/include/c++/4.7/bits/alloc_traits.h:89:43: required from ‘struct std::allocator_traits<std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/ext/alloc_traits.h:109:10: required from ‘struct __gnu_cxx::__alloc_traits<std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/bits/stl_vector.h:76:28: required from ‘struct std::_Vector_base<const std::vector<Foo>, std::allocator<const std::vector<Foo> > >’
/usr/include/c++/4.7/bits/stl_vector.h:208:11: required from ‘class std::vector<const std::vector<Foo> >’
test.cpp:11:25: required from here
/usr/include/c++/4.7/ext/new_allocator.h:83:7: error: ‘const _Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::const_reference) const [with _Tp = const std::vector<Foo>; __gnu_cxx::new_allocator<_Tp>::const_pointer = const std::vector<Foo>*; __gnu_cxx::new_allocator<_Tp>::const_reference = const std::vector<Foo>&]’ cannot be overloaded
/usr/include/c++/4.7/ext/new_allocator.h:79:7: error: with ‘_Tp* __gnu_cxx::new_allocator<_Tp>::address(__gnu_cxx::new_allocator<_Tp>::reference) const [with _Tp = const std::vector<Foo>; __gnu_cxx::new_allocator<_Tp>::pointer = const std::vector<Foo>*; __gnu_cxx::new_allocator<_Tp>::reference = const std::vector<Foo>&]’
the same code compiles fine with VS2012, but fails badly under g++ and I've not the slightest idea how to interpret the error message.
I'm surprised you don't get problems elsewhere, as this vector<const vector<Foo> > is not a possible data type.
The value_type of a std::vector must at minimum be copy constructible (C++03) or movable (C++11). If it is const, you cannot assign or move it.
The Allocator requirements used for standard containers doesn't allow for const objects to be held by the containers. C++11 17.6.3.5 "Allocator requirements" outlines the requirements for standard allocators using a set of tables which start with Table 27. In Table 27, the first definition is for the 'descriptive variables' T, U, and C which are defined as "any non-const, non-reference object type". The descriptive variables X and Y, used for the Allocator class defintions, are:
X - an Allocator class for type T
Y - the corresponding Allocator class for type U
In summary, standard Allocators are defined in terms of parameterization by non-const types.
There's an old GCC/libstdc++ bug for this (resolved as invalid) going back to 2004 (C++2003 has a similar limitation against storing const objects in containers, but it's simpler to show because the 2003 standard more straightforwardly says that the types of objects stored in containers must be 'Assignable').
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=16875
I'm using the latest available GCC build from repository. I decided to use it because some additional C++0x features. However now I stuck with something what supposes to work - I want to add new element to map via r-value. Simplified code, which demonstrates problem:
#include <tr1/unordered_map>
class X
{
public:
X (void) { /* ... */ };
X (const X& x) = delete;
X (X&& x) { /* ... */ };
};
int main (void)
{
std::tr1::unordered_map<int, X> map;
// using std::tr1::unordered_map<int, X>::value_type didn't help too
std::pair<int, X> value (1, X ());
map.insert (std::move (value));
}
Notice, that when replace X class with some primitive type like int code compiles and work just fine.
In my production code class corresponding to X doesn't have copy constructor too.
Error message is (like all template-related errors) long and unreadable and I'm not sure if it's good idea to put it here. Notify me if you want error message, so I'll update this question. Last part of message is interesting:
(...)
/usr/include/c++/trunk/ext/new_allocator.h:106:9: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int, _T2 = X, std::pair<_T1, _T2> = std::pair<const int, X>]’
In file included from /usr/include/c++/trunk/utility:71:0,
from /usr/include/c++/trunk/tr1/unordered_map:34,
from kod.cpp:1:
/usr/include/c++/trunk/bits/stl_pair.h:110:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int, _T2 = X, std::pair<_T1, _T2> = std::pair<const int, X>]’ is implicitly deleted because the default definition would be ill-formed:
/usr/include/c++/trunk/bits/stl_pair.h:110:17: error: use of deleted function ‘X::X(const X&)’
Moreover this should work, because similar bug was already fixed [C++0x] Implement emplace* in associative and unordered containers.
Maybe I'm doing something wrong? I want to be sure, that's GCC or libstdc++ bug before reporting it.
Your code looks correct to me except for your use of tr1. tr1-qualified stuff doesn't know about rvalue reference or move semantics.
I took your code, removed tr1 from the header and namespace qualifiers, and successfully compiled your code using g++-4.4 and libc++ (http://libcxx.llvm.org/). Try removing tr1.
The value_type of that unordered_map is not std::pair<int, X>. It is std::pair<const int, X>. Maybe if you use that type for the value it will work better.
decltype(map)::value_type value(1, X());
map.insert(std::move(value));
Although I don't exactly see why your code shouldn't work as is.