6

I have a class as:

#include <memory>

class Object {
    std::shared_ptr<void> object_ptr;
public:
    Object() {}

    template<typename T>
    Object(T&& object) 
        : object_ptr {new T {std::move(object)} } {}

    virtual ~Object() {};
};

My main cpp file is:

#include <iostream>
#include "Object.hpp"

class Foo {};


int main() {
    Object o {Foo{}};
}

It gives me error:

test/test.cpp:13:20:   required from here
include/Object.hpp:24:49: error: could not convert ‘{std::move<Foo&>((* & object))}’ from ‘<brace-enclosed initializer list>’ to ‘Foo’
         : object_ptr {new T {std::move(object)} } {}
                                                 ^
include/Object.hpp:24:49: error: no matching function for call to ‘std::shared_ptr<void>::shared_ptr(<brace-enclosed initializer list>)’
include/Object.hpp:24:49: note: candidates are:
In file included from /usr/include/c++/4.8/memory:82:0,
                 from include/Object.hpp:7,
                 from test/test.cpp:2:
/usr/include/c++/4.8/bits/shared_ptr.h:314:2: note: template<class _Alloc, class ... _Args> std::shared_ptr<_Tp>::shared_ptr(std::_Sp_make_shared_tag, const _Alloc&, _Args&& ...)
  shared_ptr(_Sp_make_shared_tag __tag, const _Alloc& __a,
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:314:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:265:17: note: constexpr std::shared_ptr<_Tp>::shared_ptr(std::nullptr_t) [with _Tp = void; std::nullptr_t = std::nullptr_t]
       constexpr shared_ptr(nullptr_t __p) noexcept
                 ^
/usr/include/c++/4.8/bits/shared_ptr.h:265:17: note:   no known conversion for argument 1 from ‘<type error>’ to ‘std::nullptr_t’
/usr/include/c++/4.8/bits/shared_ptr.h:257:2: note: template<class _Tp1, class _Del> std::shared_ptr<_Tp>::shared_ptr(std::unique_ptr<_Up, _Ep>&&)
  shared_ptr(std::unique_ptr<_Tp1, _Del>&& __r)
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:257:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:253:2: note: template<class _Tp1> std::shared_ptr<_Tp>::shared_ptr(std::auto_ptr<_Up>&&)
  shared_ptr(std::auto_ptr<_Tp1>&& __r);
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:253:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:248:11: note: template<class _Tp1> std::shared_ptr<_Tp>::shared_ptr(const std::weak_ptr<_Tp1>&)
  explicit shared_ptr(const weak_ptr<_Tp1>& __r)
           ^
/usr/include/c++/4.8/bits/shared_ptr.h:248:11: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:236:2: note: template<class _Tp1, class> std::shared_ptr<_Tp>::shared_ptr(std::shared_ptr<_Tp1>&&)
  shared_ptr(shared_ptr<_Tp1>&& __r) noexcept
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:236:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:226:7: note: std::shared_ptr<_Tp>::shared_ptr(std::shared_ptr<_Tp>&&) [with _Tp = void]
       shared_ptr(shared_ptr&& __r) noexcept
       ^
/usr/include/c++/4.8/bits/shared_ptr.h:226:7: note:   no known conversion for argument 1 from ‘<type error>’ to ‘std::shared_ptr<void>&&’
/usr/include/c++/4.8/bits/shared_ptr.h:218:2: note: template<class _Tp1, class> std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Tp1>&)
  shared_ptr(const shared_ptr<_Tp1>& __r) noexcept
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:218:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:206:2: note: template<class _Tp1> std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Tp1>&, _Tp*)
  shared_ptr(const shared_ptr<_Tp1>& __r, _Tp* __p) noexcept
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:206:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:184:2: note: template<class _Deleter, class _Alloc> std::shared_ptr<_Tp>::shared_ptr(std::nullptr_t, _Deleter, _Alloc)
  shared_ptr(nullptr_t __p, _Deleter __d, _Alloc __a)
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:184:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:165:2: note: template<class _Tp1, class _Deleter, class _Alloc> std::shared_ptr<_Tp>::shared_ptr(_Tp1*, _Deleter, _Alloc)
  shared_ptr(_Tp1* __p, _Deleter __d, _Alloc __a)
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:165:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:146:2: note: template<class _Deleter> std::shared_ptr<_Tp>::shared_ptr(std::nullptr_t, _Deleter)
  shared_ptr(nullptr_t __p, _Deleter __d)
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:146:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:129:2: note: template<class _Tp1, class _Deleter> std::shared_ptr<_Tp>::shared_ptr(_Tp1*, _Deleter)
  shared_ptr(_Tp1* __p, _Deleter __d)
  ^
/usr/include/c++/4.8/bits/shared_ptr.h:129:2: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:112:11: note: template<class _Tp1> std::shared_ptr<_Tp>::shared_ptr(_Tp1*)
  explicit shared_ptr(_Tp1* __p)
           ^
/usr/include/c++/4.8/bits/shared_ptr.h:112:11: note:   template argument deduction/substitution failed:
/usr/include/c++/4.8/bits/shared_ptr.h:103:7: note: std::shared_ptr<_Tp>::shared_ptr(const std::shared_ptr<_Tp>&) [with _Tp = void]
       shared_ptr(const shared_ptr&) noexcept = default;
       ^
/usr/include/c++/4.8/bits/shared_ptr.h:103:7: note:   no known conversion for argument 1 from ‘<type error>’ to ‘const std::shared_ptr<void>&’
/usr/include/c++/4.8/bits/shared_ptr.h:100:17: note: constexpr std::shared_ptr<_Tp>::shared_ptr() [with _Tp = void]
       constexpr shared_ptr() noexcept
                 ^
/usr/include/c++/4.8/bits/shared_ptr.h:100:17: note:   candidate expects 0 arguments, 1 provided
make: *** [test.o] Error 1

However if I change new T {std::move(object)} to new T (std::move(object)). It works:

class Object {
    std::shared_ptr<void> object_ptr;
public:
    Object() {}

    template<typename T>
    Object(T&& object) 
        : object_ptr {new T (std::move(object)) } {}

    virtual ~Object() {};
};

Why curly brace uniform initialization does not work here? Why is considered an initializater list in this case? Foo does not even have a constructor taking initializer list?

SwiftMango
  • 15,092
  • 13
  • 71
  • 136
  • 1
    This is a possible duplicate of [Use std::move in C++11 move constructor with uniform initialization syntax](http://stackoverflow.com/questions/15864269/use-stdmove-in-c11-move-constructor-with-uniform-initialization-syntax). – Sam Estep Jun 24 '15 at 20:17
  • Why do you `new` and not use `std::make_shared`? – Marius Bancila Jun 24 '15 at 20:21
  • See [LWG 1467](http://open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1467). – David G Jun 24 '15 at 20:21
  • @0x499602D2 such a long list... Just tested: Not working prior gcc 4.9, but **working in gcc 5.1**. Not working in clang 3.6 either – SwiftMango Jun 24 '15 at 20:23
  • @MariusBancila because `object_ptr` is `void*`. Using `make_shared` will requires casting from `shared_ptr` to `shared_ptr`. – SwiftMango Jun 24 '15 at 20:29
  • @texasbruce that will work (i.e. `object_ptr {std::make_shared(std::move(object)) }`). See this http://stackoverflow.com/questions/5913396/why-do-stdshared-ptrvoid-work. – Marius Bancila Jun 24 '15 at 20:36
  • Note that in general, you should remove that reference from the forwarding reference `T&&`. Otherwise, you'll construct lvalue-refs when passing an lvalue to the `Object(T&&)` ctor. `new std::remove_reference_t{ .. }` This isn't a problem here though, since you're passing a prvalue. – dyp Jun 24 '15 at 20:39
  • 1
    CWG 1467 will be reopened cc @0x499602D2 see https://groups.google.com/a/isocpp.org/d/topic/std-discussion/CvYHxNSzw8c/discussion – dyp Jun 24 '15 at 20:41

1 Answers1

4

This was a known issue, and a defect in the standard (see CWG #1467). The proposal has been applied to the latest working paper (§8.5.4 [dcl.init.list]/3):

List-initialization of an object or reference of type T is defined as follows:

  • If T is a class type and the initializer list has a single element of type cv U, where U is T or a class derived from T, the object is initialized from that element (by copy-initialization for copy-list-initialization, or by direct-initialization for direct-list-initialization).

Note that trunk versions of Clang and GCC accept this code.

David G
  • 94,763
  • 41
  • 167
  • 253