0

I am having trouble figuring out how to prevent recursive attempts at template substitution and explicitly force a sub-class template instantiation

The following code:

#include <boost/ptr_container/ptr_vector.hpp>

template <typename TemplateParameter>
struct ParentClass
{
    struct SubClass
    {
        boost::ptr_vector<SubClass> nodes;
    }; // this by itself works fine

    // but with combination with this
    void someMethod(boost::ptr_vector<SubClass>& otherNodes){} 
};

int main (int argc, char** argv)
{
    ParentClass<int> p;
    return 0;
}

built with boost 1.68 and gcc 7.3.1 gives the following error:

test.cpp: In instantiation of ‘struct ParentClass<int>::SubClass’:
/usr/include/boost/ptr_container/nullable.hpp:55:13:   recursively required by substitution of ‘template<class T> boost::type_traits::yes_type boost::ptr_container_detail::is_nullable(const boost::nullable<T>*) [with T = <missing>]’
/usr/include/boost/ptr_container/nullable.hpp:55:13:   required from ‘const bool boost::is_nullable<ParentClass<int>::SubClass>::value’
/usr/include/boost/mpl/if.hpp:63:11:   required from ‘struct boost::mpl::if_<boost::is_nullable<ParentClass<int>::SubClass>, ParentClass<int>::SubClass, boost::mpl::identity<ParentClass<int>::SubClass> >’
/usr/include/boost/mpl/eval_if.hpp:37:41:   required from ‘struct boost::mpl::eval_if<boost::is_nullable<ParentClass<int>::SubClass>, ParentClass<int>::SubClass, boost::mpl::identity<ParentClass<int>::SubClass> >’
/usr/include/boost/ptr_container/nullable.hpp:69:13:   required from ‘struct boost::remove_nullable<ParentClass<int>::SubClass>’
/usr/include/boost/ptr_container/nullable.hpp:80:55:   required from ‘struct boost::ptr_container_detail::void_ptr<ParentClass<int>::SubClass>’
test.cpp:11:10:   required from ‘struct ParentClass<int>’
test.cpp:16:22:   required from here
test.cpp:8:37: error: invalid use of incomplete type ‘struct boost::ptr_container_detail::void_ptr<ParentClass<int>::SubClass>’
         boost::ptr_vector<SubClass> nodes;
                                     ^~~~~
In file included from /usr/include/boost/ptr_container/detail/reversible_ptr_container.hpp:25,
                 from /usr/include/boost/ptr_container/ptr_sequence_adapter.hpp:20,
                 from /usr/include/boost/ptr_container/ptr_vector.hpp:20,
                 from test.cpp:1:
/usr/include/boost/ptr_container/nullable.hpp:75:16: note: declaration of ‘struct boost::ptr_container_detail::void_ptr<ParentClass<int>::SubClass>’
         struct void_ptr
                ^~~~~~~~

My understanding of the issue is that it tries to resolve the ParentClass and consequently tries to resolve its method - someMehtod(...) but in order to do it must resolve the SubClass which is defined. A workaround is to force an explicit instantiation by instantiating SubClass as a member of ParentClass:

struct SubClass
{
    boost::ptr_vector<SubClass> nodes;
} member;

Is there another, less hacky way? :)

The issue was not present on older version of boost, where boost::ptr_vector was implemented with void* instead of void_ptr template class.

vordhosbn
  • 333
  • 4
  • 16
  • 1
    Can you use std::vector> with the copy constructor deleted? With gcc 7.3, you should be in C++11 land and not require boost::ptr_vector anymore. – Matthieu Brucher Oct 18 '18 at 16:59
  • 1
    `std::vector>` has different ownership semantics - you can assign a `boost::ptr_vector` to another for example. It doesn't make a lot of sense since destruction of one of the vectors will destroy the objects held by it, but otherwise you are forcing every class that has a `ParentClass` or `ParentClass::SubClass` member to be non-copyable. – vordhosbn Oct 18 '18 at 17:08
  • Looks like it's specific to boost. `std::vector` is free from this problem. – SergeyA Oct 18 '18 at 17:10
  • Yes, as noted, It appeared in recent boost versions. :) They changed boost::ptr_vector implementation from `void*` to `boost::ptr_container_detail::void_ptr` – vordhosbn Oct 18 '18 at 17:11
  • @vordhosbn It seems to me you are dealing with boost bug. Less hacky way would be to file a bugreport with them. Or just switch to vector of pointers, honestly, I do not see much benefit in using boost. – SergeyA Oct 18 '18 at 17:14
  • @vorbhosbn so even more reasons to use `std::vector>`. You want to have the object non-copyable because of the enforced memory management. That's a good thing. – Matthieu Brucher Oct 18 '18 at 17:14
  • 1
    @MatthieuBrucher the most important reason to use vector of raw pointers is that not every pointer is an owning pointer. OP didn't give use background, and it might be very reasonable to have a vector of non-owning pointers. – SergeyA Oct 18 '18 at 17:17
  • I too think that might be a bug in boost, but still I was really surprised that it was possible to explicitly instantiate SubClass by making it also a member of ParentClass. Just wondered if there is some "metamagic" feat you can use to achieve the same. :) – vordhosbn Oct 18 '18 at 17:28
  • @SergeyA: same applies for vector. By default, it owns the object, as would ptr_vector. But you can replace or extract elements from it. So not a valid reason not to use vector. – Matthieu Brucher Oct 18 '18 at 18:41
  • @MatthieuBrucher, no, ptr_vector doesn't own it's elements. There are reasons to use a non-owning pointer, or container of thereof. `unique_ptr` unequivocally owns the object, it is not 'by default', it is just this. So yes, it is a valid reason not to use it. – SergeyA Oct 18 '18 at 18:43
  • It does: https://stackoverflow.com/questions/11666024/how-does-ptr-vector-manage-memory If it didn't own the objects, what would be the point of ptr_vector against vector<*>? Seriously? – Matthieu Brucher Oct 18 '18 at 18:47
  • What happens if you don't define `someMethod` inline? Just declare it (replace `{}` with `;`, then define int outside the class. – Filipp Oct 19 '18 at 00:27
  • @Filipp Then the recursive template substitution error moves from the header to the cpp. – vordhosbn Oct 19 '18 at 14:47
  • @vordhosbn Which .cpp? Did you define the method below the class in the.h? `template void ParentClass::someMethod(boost::ptr_vector::SubClass>&) {}` – Filipp Oct 21 '18 at 01:18
  • Sorry, @Filipp was referring to the original code that provoked this question (which has the class definition in a header and the code using it in a cpp). Anyways, moving the method definition away from the class definition does not prevent the error. – vordhosbn Oct 22 '18 at 10:57
  • I suspect the problem is that `SubClass` is not "complete enough" for `ptr_vector` at the point it is needed. Try defining `SubClass`'s destructor out of line, same as you just did with `someMethod`. – Filipp Oct 22 '18 at 12:28
  • Indeed this is the issue, but moving the destructor out doesn't help either... – vordhosbn Oct 22 '18 at 12:48

0 Answers0