1

I have a strange compilation problem that I can't understand.

//I know, you should never derive from the STL Library
template<class T>
class SharedClass : private shared_ptr<T>
{
public:
    template<class T2>
    SharedClass(T2&& t2) :
    shared_ptr<T>(make_shared<T>(move(t2)))
    {}

    virtual ~SharedClass() 
    {}
};

class A
{
public:
    typedef function<void(SharedClass<A>)> Callback;

    A(Callback callback)
    { }
};

main.cpp

SharedClass<A> shared([](SharedClass<A>){ });

Compile Log:

In file included from /usr/include/x86_64-linux-gnu/c++/7/bits/c++allocator.h:33:0,
                 from /usr/include/c++/7/bits/allocator.h:46,
                 from /usr/include/c++/7/string:41,
                 from /usr/include/c++/7/bits/locale_classes.h:40,
                 from /usr/include/c++/7/bits/ios_base.h:41,
                 from /usr/include/c++/7/ios:42,
                 from /usr/include/c++/7/ostream:38,
                 from /usr/include/c++/7/iostream:39,
                 from main.cpp:9:
/usr/include/c++/7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = A; _Args = {SharedClass<A>}; _Tp = A]’:
/usr/include/c++/7/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT>>::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = A; _Args = {SharedClass<A>}; _Tp = A; std::allocator_traits<std::allocator<_CharT>>::allocator_type = std::allocator<A>]’
/usr/include/c++/7/bits/shared_ptr_base.h:526:39: required from ‘std::_Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp>::_Sp_counted_ptr_inplace(_Alloc, _Args&& ...) [with _Args = {SharedClass<A>}; _Tp = A; _Alloc = std::allocator<A>; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’ /usr/include/c++/7
/bits/shared_ptr_base.h:637:4:   required from ‘std::__shared_count<_Lp>::__shared_count(std::_Sp_make_shared_tag, _Tp*, const _Alloc&, _Args&& ...) [with _Tp = A; _Alloc = std::allocator<A>; _Args = {SharedClass<A>}; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’
/usr/include/c++/7/bits/shared_ptr_base.h:1295:35:   required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>; _Args = {SharedClass<A>}; _Tp = A; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’
/usr/include/c++/7/bits/shared_ptr.h:344:64:   required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>; _Args = {SharedClass<A>}; _Tp = A]’
/usr/include/c++/7/bits/shared_ptr.h:690:14:   [ skipping 9 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/usr/include/c++/7/bits/shared_ptr_base.h:1295:35:   required from ‘std::__shared_ptr<_Tp, _Lp>::__shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>; _Args = {main()::<lambda(SharedClass<A>)>}; _Tp = A; __gnu_cxx::_Lock_policy _Lp = (__gnu_cxx::_Lock_policy)2]’
/usr/include/c++/7/bits/shared_ptr.h:344:64:   required from ‘std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...) [with _Alloc = std::allocator<A>; _Args = {main()::<lambda(SharedClass<A>)>}; _Tp = A]’ /usr/include/c++/7/bits/shared_ptr.h:690:14:   required from ‘std::shared_ptr<_Tp> std::allocate_shared(const _Alloc&, _Args&& ...) [with _Tp = A; _Alloc = std::allocator<A>; _Args = {main()::<lambda(SharedClass<A>)>}]’
/usr/include/c++/7/bits/shared_ptr.h:706:39:   required from ‘std::shared_ptr<_Tp> std::make_shared(_Args&& ...) [with _Tp = A; _Args = {main()::<lambda(SharedClass<A>)>}]’
main.cpp:21:33:   required from ‘SharedClass<T>::SharedClass(T2&&) [with T2 = main()::<lambda(SharedClass<A>)>; T = A]’
main.cpp:39:48: required from here
/usr/include/c++/7/ext/new_allocator.h:136:4: error: no matching function for call to ‘A::A(SharedClass)’
   { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:33:5: note: candidate: A::A(A::Callback)
      A(Callback callback)
      ^ main.cpp:33:5: note:   no known conversion for argument 1 from ‘SharedClass’ to ‘A::Callback {aka std::function)>}’
main.cpp:28:7: note: candidate: constexpr A::A(const A&)
  class A
        ^ main.cpp:28:7: note:   no known conversion for argument 1 from ‘SharedClass’ to ‘const A&’
main.cpp:28:7: note: candidate: constexpr A::A(A&&)
main.cpp:28:7: note:   no known conversion for argument 1 from ‘SharedClass’ to ‘A&&’

If I change the constructor of the SharedClass class like this SharedClass (T2 t2) it compiles.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • Please [edit] your question to include the full error message the compiler is reporting. – YSC Jan 08 '20 at 14:18
  • Please, give us fully working example – NutCracker Jan 08 '20 at 14:25
  • I presume this is a minimal example to illustrate the issue (which, by the way, thank you). I don't understand the use case, and why you wouldn't use [`std::enable_shared_from_this`](https://en.cppreference.com/w/cpp/memory/enable_shared_from_this) and a static factory function. May have to use a public constructor with a private-key-tag idiom. – Eljay Jan 08 '20 at 14:26

1 Answers1

2

As your lambda takes its parameters by value it needs to be able to create copies of those parameters, it does this by calling the move constructor. As you have defined a templated constructor with the same signature as the move constructor the compiler attempts to use it as a move constructor. Adding default copy (for completeness) and move constructors fixes the issue:

SharedClass(const SharedClass&) = default;
SharedClass(SharedClass&&) = default;

Your existing constructor doesn't work as a move constructor because there is no constructor for A which takes a SharedClass<A> parameter so make_shared<A> doesn't compile when passed a SharedClass<A> parameter.

Alan Birtles
  • 32,622
  • 4
  • 31
  • 60
  • Default move operations are not generated due to defined destructor, not templated ctor. Removing dtor is enough this code to work. – rafix07 Jan 08 '20 at 14:56
  • @rafix07 The virtual destructor is necessary, because the `SharedClass` was created to be derived. – Parminder Singh Jan 08 '20 at 15:01