16

I encountered this problem maintaining a port for a large (relative to the size of our team) project, but it was simple to create a small example. stackoverflow.cpp:

#include<iostream>
#include<string>
#include<stack>

using namespace std;


int main (int argc, char *argv[]) {
  stack<const string> strstack;
  string str("Hello, world");
  strstack.push(str);
  cout << strstack.top() << endl;
  return 0;
}

Looks correct, right? MSVS thinks so too. However:

g++ stackoverflow.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/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 stackoverflow.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const std::basic_string<char> >’:
/usr/include/c++/4.7/bits/allocator.h:89:11:   required from ‘class std::allocator<const std::basic_string<char> >’
/usr/include/c++/4.7/bits/stl_deque.h:489:61:   required from ‘class std::_Deque_base<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >’
/usr/include/c++/4.7/bits/stl_deque.h:728:11:   required from ‘class std::deque<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >’
/usr/include/c++/4.7/bits/stl_stack.h:98:46:   required from ‘class std::stack<const std::basic_string<char> >’
stackoverflow.cpp:9:23:   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::basic_string<char>; __gnu_cxx::new_allocator<_Tp>::const_pointer = const std::basic_string<char>*; __gnu_cxx::new_allocator<_Tp>::const_reference = const std::basic_string<char>&]’ 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::basic_string<char>; __gnu_cxx::new_allocator<_Tp>::pointer = const std::basic_string<char>*; __gnu_cxx::new_allocator<_Tp>::reference = const std::basic_string<char>&]’
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::deallocate(__gnu_cxx::new_allocator<_Tp>::pointer, __gnu_cxx::new_allocator<_Tp>::size_type) [with _Tp = const std::basic_string<char>; __gnu_cxx::new_allocator<_Tp>::pointer = const std::basic_string<char>*; __gnu_cxx::new_allocator<_Tp>::size_type = long unsigned int]’:
/usr/include/c++/4.7/bits/stl_deque.h:540:2:   required from ‘void std::_Deque_base<_Tp, _Alloc>::_M_deallocate_node(_Tp*) [with _Tp = const std::basic_string<char>; _Alloc = std::allocator<const std::basic_string<char> >]’
/usr/include/c++/4.7/bits/stl_deque.h:643:2:   required from ‘void std::_Deque_base<_Tp, _Alloc>::_M_destroy_nodes(_Tp**, _Tp**) [with _Tp = const std::basic_string<char>; _Alloc = std::allocator<const std::basic_string<char> >]’
/usr/include/c++/4.7/bits/stl_deque.h:566:4:   required from ‘std::_Deque_base<_Tp, _Alloc>::~_Deque_base() [with _Tp = const std::basic_string<char>; _Alloc = std::allocator<const std::basic_string<char> >]’
/usr/include/c++/4.7/bits/stl_deque.h:781:15:   required from ‘std::deque<_Tp, _Alloc>::deque() [with _Tp = const std::basic_string<char>; _Alloc = std::allocator<const std::basic_string<char> >]’
stackoverflow.cpp:9:23:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:100:9: error: invalid conversion from ‘const void*’ to ‘void*’ [-fpermissive]
In file included from /usr/include/c++/4.7/ext/new_allocator.h:34:0,
                 from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34,
                 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 stackoverflow.cpp:1:
/usr/include/c++/4.7/new:97:6: error:   initializing argument 1 of ‘void operator delete(void*)’ [-fpermissive]
In file included from /usr/include/c++/4.7/deque:63:0,
                 from /usr/include/c++/4.7/stack:61,
                 from stackoverflow.cpp:3:
/usr/include/c++/4.7/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, const _T2&) [with _T1 = const std::basic_string<char>; _T2 = std::basic_string<char>]’:
/usr/include/c++/4.7/bits/stl_uninitialized.h:77:3:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>; _ForwardIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>; bool _TrivialValueTypes = false]’
/usr/include/c++/4.7/bits/stl_uninitialized.h:119:41:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>; _ForwardIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>]’
/usr/include/c++/4.7/bits/stl_uninitialized.h:260:63:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>; _ForwardIterator = std::_Deque_iterator<const std::basic_string<char>, const std::basic_string<char>&, const std::basic_string<char>*>; _Tp = const std::basic_string<char>]’
/usr/include/c++/4.7/bits/stl_deque.h:841:9:   required from ‘std::deque<_Tp, _Alloc>::deque(const std::deque<_Tp, _Alloc>&) [with _Tp = const std::basic_string<char>; _Alloc = std::allocator<const std::basic_string<char> >; std::deque<_Tp, _Alloc> = std::deque<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >]’
/usr/include/c++/4.7/bits/stl_stack.h:130:14:   required from ‘std::stack<_Tp, _Sequence>::stack(const _Sequence&) [with _Tp = const std::basic_string<char>; _Sequence = std::deque<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >]’
stackoverflow.cpp:9:23:   required from here
/usr/include/c++/4.7/bits/stl_construct.h:85:7: error: invalid static_cast from type ‘const std::basic_string<char>*’ to type ‘void*’

With stack<string> strstack; it's all clean. Is this a g++ bug? Is there a workaround so that we won't have to drop the const until the bug is fixed?

01d55
  • 1,872
  • 13
  • 22
  • 4
    The fact that it compiles in MSVC is actually due to a bug in the way MSVC does template instantiation. (And no little bug, they won't fix it because it would break a lot of existing code) – Mooing Duck Jun 13 '13 at 21:41

2 Answers2

28

The members of a standard container have to be copy assignable or movable (C++11). If the type is const it fails the requirements.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • Ah, so accepting this sort of thing is MSVS being non-standard? In that case, I'm comfortable changing our code. – 01d55 Nov 03 '12 at 22:18
  • 4
    It depends on what you are doing. If you are not using all the functionality of a container, you might get away with not fulfilling all the requirements. An implementation is not required to fail, even if it is allowed. – Bo Persson Nov 03 '12 at 22:21
  • 3
    -1 AFAIK this answer is wrong. The only general C++11 requirement I know of for container elements is that they be Destructible (and moving didn't exist in C++03). Would you care to cite chapter and verse from the standard, please. – Cheers and hth. - Alf Dec 04 '13 at 22:44
  • @Cheersandhth.-Alf Sequence Container Requirements (table 100 in the draft I'm looking at) lists expression `a = il` as requiring that T be both copy insertable and copy assignable. `a.insert` requires copy assignable for vector and deque. Allocator Aware Container Requirements (table 99) also has an expression, `a = t`, requiring copy assignable. – 01d55 Aug 04 '14 at 20:26
  • 2
    @01d55: assignment requires assignable items yes. that does *not* mean that “members of a standard container have to be copy assignable or movable”. it means that if you assign to the container, then it must have assignable items (and ditto for other operations, each operation that you actually use must be possible). this answer is just wrong, precluding e.g. a `std::list` of `const` items. – Cheers and hth. - Alf Aug 04 '14 at 20:34
  • 1
    @Cheersandhth.-Alf I agree, though not official http://en.cppreference.com/w/cpp/container/vector says: "The requirements that are imposed on the elements depend on the actual operations performed on the container. Generally, it is required that element type is a complete type and meets the requirements of Erasable, but many member functions impose stricter requirements." If we're allowing 2 sentence answers now they should at least come with a valid source. This answer should have been a comment at best. – Jonathan Mee May 16 '16 at 17:47
2

It is possible for the container to hold pointers (or preferably, smart pointers) to constants. This maintains the desired const semantics, although perhaps the syntax is a bit more clunky.

For example:

stack<shared_ptr<string const>> s;
s.push(make_shared<string>("foo"));
cout << *s.top() << endl;

For more info see this question.

Community
  • 1
  • 1
Kevin
  • 8,312
  • 4
  • 27
  • 31