3

Why does this fail to compile g++4.6 and g++4.7? I am trying to get a mapping of string to thread specific storage. I had something like this working in boost 1.48 I believe. Actually, it is not related to the version of boost, but the flag -std=c++0x. If that is not present it compiles. So, looking for interpretation of error and how to work around it.

Thanks

#include <map>
#include <boost/thread/tss.hpp>
#include <boost/shared_ptr.hpp>

int main(int argc, char** argv) {
  typedef boost::thread_specific_ptr< int > Tss_int_ptr;
  typedef std::map< std::string, Tss_int_ptr > Tss_int_map_t;
  Tss_int_map_t tmap;
  return 0;
}

The error message follows.

g++-4.7 -g -std=c++0x -I"/home/someone/open_source/admin/install/boost_1_52_0/include" -c  ~/tmp/fail.cpp
In file included from /usr/include/c++/4.7/bits/stl_algobase.h:65:0,
             from /usr/include/c++/4.7/bits/stl_tree.h:63,
             from /usr/include/c++/4.7/map:60,
             from /home/someone/tmp/fail.cpp:1:
/usr/include/c++/4.7/bits/stl_pair.h: In instantiation of ‘struct std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> >’:
/usr/include/c++/4.7/bits/stl_tree.h:133:12:   required from ‘struct std::_Rb_tree_node<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >’
/usr/include/c++/4.7/bits/stl_tree.h:1082:4:   required from ‘void std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_erase(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type) [with _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> >; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >*]’
/usr/include/c++/4.7/bits/stl_tree.h:646:9:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::~_Rb_tree() [with _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> >; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> > >]’
/usr/include/c++/4.7/bits/stl_map.h:90:11:   required from here
/usr/include/c++/4.7/bits/stl_pair.h:119:17: error: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const std::basic_string<char>; _T2 = boost::thread_specific_ptr<int>; std::pair<_T1, _T2> = std::pair<const std::basic_string<char>, boost::thread_specific_ptr<int> >]’ declared to take const reference, but implicit declaration would take non-const

2 Answers 2

5

thread_specific_ptr declares these members, in order to make the class non-copyable (note the non-const parameters):

private:
    thread_specific_ptr(thread_specific_ptr&);
    thread_specific_ptr& operator=(thread_specific_ptr&);

In C++03 std::pair does not have a copy constructor declared, so one is implicitly generated if needed by the program. A std::pair<X, thread_specific_ptr> is not copyable, because one of its members is not copyable, so if the implicit copy constructor was used it would be an error.

In C++11 std::pair has a copy constructor, which is explicitly defaulted. It has the signature:

pair(const pair&) = default;

The compiler error is telling you that the implicitly-generated copy constructor would have this signature, because the thread_specific_ptr copy constructor signature takes a non-const reference:

pair(pair&) = default;

Because the defaulted constructor does not have the same signature as what would have be implicitly declared, the copy constructor is ill-formed.

So in both cases the pair<X, thread_specific_ptr> is not copyable, but in C++11 the error is noticed sooner, even though you don't try to copy the object.

If boost::thread_specific_ptr used the normal C++11 idiom for making a class non-copyable the code would work:

thread_specific_ptr(const thread_specific_ptr&) = delete;
thread_specific_ptr& operator=(const thread_specific_ptr&) = delete;

So I would report this as a bug to Boost. In C++11 mode the copy operations should be deleted.

As a workaround you can wrap the type in your own type with deleted copy operations, then use that instead:

template<typename T>
struct TSS : boost::thread_specific_ptr<T>
{
  TSS() = default;
  TSS(void (*f)(T*)) : boost::thread_specific_ptr<T>(f) { }
  TSS(const TSS&) = delete;
  TSS& operator=(const TSS&) = delete;
};

Now you can use this and your code will compile:

typedef TSS< int > Tss_int_ptr;
Sign up to request clarification or add additional context in comments.

1 Comment

Interesting - I saw that thread_specific_ptr is inherited from boost::noncopyable in boost 1.34, but not in 1.52?
2

Boost::thread-specific_ptr class is non-copyable in some boost versions, which means it can't be used in c++03 STL containers. I guess that's the root of the problem, and why changing c++0x flags fixes it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.