I just ran across some unexpected and frustrating behaviour while working on a C++ project. My actual code is a tad more complicated, but the following example captures it just as well:
class Irritating
{
public: Irritating() {}
private: Irritating(const Irritating& other) {}
};
const Irritating singleton; // Works just fine.
const Irritating array[] = {Irritating()}; // Compilation error.
int main()
{
return 0;
}
Trying to compile this produces the following error (GCC version thrown in just in case):
[holt@Michaela irritating]$ g++ --version
g++ (GCC) 4.6.3 20120306 (Red Hat 4.6.3-2)
Copyright (C) 2011 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
[holt@Michaela irritating]$ g++ test.cpp
test.cpp:4:11: error: ‘Irritating::Irritating(const Irritating&)’ is private
test.cpp:8:41: error: within this context
[holt@Michaela irritating]$
The offending object, unfortunately, is from an external library and outside my control. My current workaround is to use an array of pointers; it works, but it feels a bit hackish and adds a needless layer of indirection. Is there a better way to do this?
Also: The array is constant and global (well, class-static in the actual code); why isn't it being initialized in place? Is this expected C++ behaviour, or a bug/quirk of GCC?
Update: Installed Clang just to see if it would agree with GCC. Sadly, it did:
[holt@Michaela irritating]$ clang test.cpp
test.cpp:8:29: warning: C++98 requires an accessible copy constructor for class 'Irritating' when binding a reference to a temporary; was private
[-Wbind-to-temporary-copy]
const Irritating array[] = {Irritating()};
^
test.cpp:4:11: note: declared private here
private: Irritating(const Irritating& other) {}
^
test.cpp:8:29: error: calling a private constructor of class 'Irritating'
const Irritating array[] = {Irritating()};
^
test.cpp:4:11: note: declared private here
private: Irritating(const Irritating& other) {}
^
1 warning and 1 error generated.
[holt@Michaela irritating]$
Tried just creating the array with a size? Default ctor should be called.
As in this source
which demonstrates initialized
foo
in an array with no default copy constructor.If your problem is that you actually want to construct the
foo
with a non-default constructor, this can be done as well, but it is much harder, and that isn't what your question asked. In any case, here is a very, very rough sketch of the kind of stuff needed to create an array-like structure that supports emplaced construction of its elements. It is far from finished or compiling, but the basic technique should be sound:The idea is that we create an array like construct that is actually an array of
union
to bothT
and achar
. We thus avoid actually constructing ourT
, while still having proper alignment and such for one. Assumingchar
has no non-trivial alignment, the resulting buffer is binary-compatible with aT[]
.We then take as a construction argument
emplacer
objects, which act as packages for arbitrary construction arguments. For annoying reasons, these objects need to create a temporary copy of some of their parameters (I cannot figure out how to avoid the lifetime issues... maybe I'm missing something).The constructor of the
my_array
takes any number ofemplacers
and proceeds to construct the contents ofbuff
based on their arguments.You'd create your array something like this:
a bit more work would allow default construction of uninitialized objects in the array.
But this is really, really tricky to write correctly.
Assuming the copy constructor of
Irritating
is disabled because it is expensive than perhaps it is best to manage them by reference:You could use
shared_ptr
instead ofunique_ptr
depending on the usage pattern.(If you could modify Irritating you could give it a move constructor, take a look at move semantics)
If you really want them constructed in place than you could use
aligned_storage
to make an array of storage for them and then placement new them in place. This would produce almost identical compiled code to what you want to do with your original request, but it is a little messier:(Dont forget to placement delete them at program exit.)
Because individual array elements are initialized by copy-initialization from the initializers specified through =
{...}
syntax. See 8.5/12 (C++03)Copy-initialization requires copy constructor (even if it won't actually use it).
In practice, if you make your code compile by making the copy constructor public, the compiler will probably end up initializing your array elements in place, without using the copy constructor. Nevertheless, the formal rules of abstract language call for copy-initialization in this context.