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*>.
Related
#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?
A member is defined as
std::shared_ptr<std::array<std::string, 6> > exit_to;
which points to additional data shared among others.
When try to initiate the pointer "exit_to". The correct way is
node_knot.exit_to = std::make_shared<std::array<std::string, 6> >();
But it's in another file and I'd like to keep the pointer type consistent, something like this:
node_knot.exit_to = std::make_shared<decltype(*node_knot.exit_to)>();
But won't compile:
/usr/include/c++/4.6/bits/shared_ptr_base.h:798:54: error: '__p'
declared as a pointer to a reference of type
'std::array<std::basic_string<char>, 6> &'
__shared_ptr(const __shared_ptr<_Tp1, _Lp>& __r, _Tp* __p)
^ /usr/include/c++/4.6/bits/shared_ptr.h:93:31: note: in instantiation
of template class
'std::__shared_ptr<std::array<std::basic_string<char>, 6> &, 1>'
requested here
class shared_ptr : public __shared_ptr<_Tp>
^ ../node_booker.h:757:20: note: in
instantiation of template class
'std::shared_ptr<std::array<std::basic_string<char>, 6> &>' requested
here
n.exit_to = std::make_shared<decltype(*n.exit_to)>();
I'm under Ubuntu 12.10, clang++ 3.2, with --std=c++11
You need to remove the reference from the type you are passing to make_shared.
The following should work:
node_knot.exit_to = std::make_shared<std::remove_reference<decltype(*node_knot.exit_to)>::type>();
The problem is that the type of *exit_to is a reference, and you can't have a shared_ptr to a reference.
You could remove the reference, but instead of finding the type returned by operator* and then stripping the reference off it, it's probably easier to just ask the shared_ptr what type it contains:
node_knot.exit_to = std::make_shared<decltype(node_knot.exit_to)::element_type>();
The nested element_type is the type stored by the shared_ptr.
Another option would be to add a typedef to the class and use it consistently wherever you need it:
typedef std::array<std::string, 6> string_array;
std::shared_ptr<string_array> exit_to;
// ...
node_knot.exit_to = std::make_shared<Node::string_array>();
This is a lot more readable than using decltype
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've been messing around with Qt and C++ for a while, but I've run into this error and can't seem to figure out why it crops up. There are a lot of other questions that have been answered out there with the const void* conversion error message, but I can't really see how the explanations help in my case, so here goes:
I have a reimplementation 'MyTypeManager' of QList< MyType *const>, so a list of const pointers to non-const MyTypes. However, when a function in my reimplementation, addMyType is called
void MyTypeManager::addMyType(MyType *const var)
{
this->append(var);
}
the following error(s) occur:
In file included from /usr/include/qt4/QtCore/QList:1:0,
from ../qtsdlthread/mytypemanager.h:4,
from ../qtsdlthread/mytypemanager.cpp:1:
/usr/include/qt4/QtCore/qlist.h: In member function ‘void QList<T>::node_construct(QList<T>::Node*, const T&) [with T = MyType* const]’:
/usr/include/qt4/QtCore/qlist.h:499:13: instantiated from ‘void QList<T>::append(const T&) [with T = MyType* const]’
../qtsdlthread/mytypemanager.cpp:20:26: instantiated from here
/usr/include/qt4/QtCore/qlist.h:359:58: error: invalid conversion from ‘const void*’ to ‘void*’
/usr/include/qt4/QtCore/qlist.h: In member function ‘void QList<T>::node_copy(QList<T>::Node*, QList<T>::Node*, QList<T>::Node*) [with T = MyType* const]’:
/usr/include/qt4/QtCore/qlist.h:666:9: instantiated from ‘QList<T>::Node* QList<T>::detach_helper_grow(int, int) [with T = MyType* const]’
/usr/include/qt4/QtCore/qlist.h:497:48: instantiated from ‘void QList<T>::append(const T&) [with T = MyType* const]’
../qtsdlthread/mytypemanager.cpp:20:26: instantiated from here
/usr/include/qt4/QtCore/qlist.h:386:17: error: invalid conversion from ‘const void*’ to ‘void*’
20:26 in mytypemanager is the this->append line posted above.
From the documentation:
QList's value type must be an assignable data type.
Alas MyType *const is not assignable. You have several remedies:
1. Make T a mutable pointer
2. Make T a pointer to your const pointer:
typedef MyType *const Element
void MyTypeManager::addMyType(Element var)
{
Element* ptr2ptr = new Element(var);
this->append(ptr2ptr);
}
But now you have 2 levels of memory management to worry about.
3. (Dangerous) Make T=MyType* and const-cast MyType *const into MyType *:
this->append(const_cast<MyType *>(var));
This will only work if you are certain var was originally created as a MyType* variable.
The following code won't compile on Mac OS X 10.6; it gives an error in stl_algobase.h following a list of "instantiated from [...]" messages.
#include <vector>
int main( void )
{
std::vector<int*> *v = new std::vector<int*>( 1, NULL );
return 0;
}
In order to make it compile, I have to place an explicit cast around NULL, i.e. replace it with (int*)(NULL).
That looks kind of awkward, though, and I doubt it should be really necessary; it also makes my source code look quite weird with structures and longer type names nested in namespaces, etc.
EDIT (Error message):
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h: In static member function 'static _OutputIterator std::__fill_n<true>::fill_n(_OutputIterator, _Size, const _Tp&) [with _OutputIterator = int**, _Size = int, _Tp = int]':
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h:665: instantiated from '_OutputIterator std::fill_n(_OutputIterator, _Size, const _Tp&) [with _OutputIterator = int**, _Size = int, _Tp = int]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:184: instantiated from 'void std::__uninitialized_fill_n_aux(_ForwardIterator, _Size, const _Tp&, std::__true_type) [with _ForwardIterator = int**, _Size = int, _Tp = int]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:219: instantiated from 'void std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = int**, _Size = int, _Tp = int]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:306: instantiated from 'void std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>) [with _ForwardIterator = int**, _Size = int, _Tp = int, _Tp2 = int*]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:790: instantiated from 'void std::vector<_Tp, _Alloc>::_M_initialize_dispatch(_Integer, _Integer, std::__true_type) [with _Integer = int, _Tp = int*, _Alloc = std::allocator<int*>]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:261: instantiated from 'std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const _Alloc&) [with _InputIterator = int, _Tp = int*, _Alloc = std::allocator<int*>]'
/Users/JayZ/projects/C++/test/main.cpp:6: instantiated from here
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h:641: error: invalid conversion from 'const int' to 'int*'
If that could help...
What's happening is that the compiler has to select between one of the vector constructor overloads that allows more than one argument:
explicit vector(size_type n, const T& value = T(),
const Allocator& = Allocator());
template <class InputIterator>
vector(InputIterator first, InputIterator last,
const Allocator& = Allocator());
The first overload (which you want to match) requires that the arguments be converted. The second requires no conversion, since the 2 arguments are the same type. So that's the overload the compiler is choosing. Unfortunately, the int type doesn't have all the operations that the second constructor uses for those arguments.
Forcing the arguments to be different types by casting NULL to int* or by making the first argument unsigned (1U) as other answers correctly suggest will avoid the problem by forcing the compiler to select the first constructor option.
Update:
When I compile this with MinGW 4.5.2 I get compile errors (different ones, but for the same reason, I believe). However, when I build with MSVC (2008 or 2010), I get no errors. When I look into the code that MSVC generates, it is actually matching the second overload (with 2 iterators) not the 'size and default value' overload.
"Aha", I think, "this is going to crash at runtime somewhere, because iterators with the value 1 and 0 don't make any sense".
However, when the 'iterator' types of that constructor happen to be int, the MSVC implementation performs a 'count/initial value' construction of the vector.
The standard says what that constructor must do when the InputIterator arguments of the constructor meet the requirements of an InputIterator. Since int doesn't meet the requirements of an InputIterator, the implementation has some free reign to do something else. The standard says that "An implementation can declare additional non-virtual member function signatures within a class" (17.4.4.4), and this would cover MSVC's behavior, I believe. The behavior that you're seeing and that I see with GCC 4.5.2, producing an error diagnostic, is also permitted by the standard.
I'm sure MSVC's behavior is what most users intend and want (until they move to the code to some other compiler maybe). And it's certainly clever.
The code you have posted compiles for me on OS X 10.6.7. I added in some assertions just to prove that it is doing what it looks like it should be...
bash-3.2$ cat test.cpp
#include <vector>
#include <cassert>
int main()
{
std::vector<int*> *v = new std::vector<int*>( 1, NULL );
assert(v != NULL);
assert(v->size() == 1);
assert(v->at(0) == NULL);
assert(false);
return 0;
}
bash-3.2$
bash-3.2$ g++ -Wall -Wextra -Weffc++ test.cpp
bash-3.2$ ./a.out
Assertion failed: (false), function main, file test.cpp, line 10.
Abort trap
bash-3.2$ g++ --version
i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5664)
Copyright (C) 2007 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
The assert(false) is deliberate :-)
With thanks to Mackie Messer's answer I can reproduce your error with -m32. This line fixes it (and is neater than casting IMHO):
std::vector<int*> *v = new std::vector<int*>(1U, NULL);
Hope this helps.