12
votes

I'm trying to use the std::shared_ptr in clang++(clang version 3.1 (trunk 143100)) using libstdc++(4.6.1). I have a little demo program:

#include <memory>

int main()
{
    std::shared_ptr<int> some(new int);
    std::shared_ptr<int> other(some);
    return 0;
}

which can be build using:

clang++ -std=c++0x -o main main.cpp

and gives the following error output:

main.cpp:6:23: error: call to deleted constructor of 'std::shared_ptr<int>'
    std::shared_ptr<int> other(some);
                         ^     ~~~~
/usr/include/c++/4.6/bits/shared_ptr.h:93:11: note: function has been explicitly marked
deleted here
class shared_ptr : public __shared_ptr<_Tp>

For some reason it needs the constructor which is deleted because a move constructor is provided (which is correct behaviour). But why does it work compile with (g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1.)? Somebody any ideas on how to fix this?

2
/usr/include/c++/4.6 is usually not where G++ looks for its system headers. Try g++ -v yourfile.cpp to see G++'s search path.Mat
@Mat: Is it not? g++ usually looks for c++ standard headers inside /usr/include/c++/$(version) AFAIK, so g++4.6 would look in that directory. As of clang++, it probably depends on how it was compiled/configuredDavid Rodríguez - dribeas

2 Answers

20
votes

The implicitly-declared copy constructor for shared_ptr is deleted because shared_ptr has a move constructor or a move assignment operator (or both), per C++11 12.8p7:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4).

GCC 4.6.x does not implement this rule, which came into the C++11 working paper very late in the process as N3203=10-0193. The shared_ptr in libstdc++ 4.6.x was correct at the time it was written, but C++11 changed after that.. Boost had exactly the same issue with it's shared_ptr, and this is one of the common incompatibilities between GCC and Clang.

Adding a defaulted copy constructor and copy assignment operator to shared_ptr will fix the problem.

6
votes

The standard library headers for gcc 4.6 seem to be wrong in this case as the standard requires the following constructor (§20.7.2.2.1/16):

shared_ptr(const shared_ptr& r) noexcept;

Which is a copy-constructor that seem to be missing from the gcc implementation. The implementation I have at hand (g++-4.6.0) offers (in bits/shared_ptr.h):

template<typename _Tp1>
shared_ptr(const shared_ptr<_Tp1>& __r)

But does not have a proper copy-constructor (a templated constructor cannot be used by the compiler as a copy constructor). I find it strange, though, that such an error would come up with a production compiler...

EDIT I have been trying to figure out why exactly g++ 4.6 compiles the above code with it's own standard library. It seems that it is picking up the templated constructor as a viable overload for copy construction, which made me look back to the standard to verify whether that is a bug --I have always been under the impression that a template could not been used as a copy constructor-- or not.

In the C++03 standard, there is a footnote 106)

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declara- tion of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy construc- tors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

That footnote is not present in C++11. Footnotes are not normative, so there has to be other quote supporting that note. A couple of paragraphs below there you can find:

12.8/3 A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to perform the copy of a class object to an object of its class type.

The first part of the paragraph means that a constructor cannot take the same type as argument (copy constructors take references, and allowing such constructor would cause an ambiguity, that and the fact that it would require copying into the argument, for which the best overload --assuming that this inhibited the copy constructor-- would be the same function, which in turns would require... infinite loop).

The second part of the clause states that a templated constructor cannot be used to create copies of the type, which seems to be the normative section supporting footnote 106). Now, that has been carefully reworded in C++11 to state:

12.8/6 A declaration of a constructor for a class X is ill-formed if its first parameter is of type (optionally cv-qualified) X and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to produce such a constructor signature.

Now, what this means is that the restriction a template cannot be used to copy has been removed, and replaced by the less strict restriction: a template will not be instantitated to generate a constructor of the form S( S ), i.e. one that would cause an ambiguity with the copy constructor.

With this lesser restriction, it seems that the templated constructor above can actually be used as a copy-constructor, as the signature that it generates is compatible. This supports the behavior of the g++ 4.6 compiler when processing the bits/shared_ptr.h header, and would imply that clang++ is failing to use the template as a valid constructor.

Now the next question is whether the standard library implementation shipped with g++4.6 is actually compliant or not. I cannot say off the top of my head. On one side, it is missing the signature of constructor that I mentioned above, so you can argue that it is not compliant. But on the other hand, a compliant compiler should pick up the templated constructor to achieve the same functionality, and the implementation would behave as-if that constructor was present.