18

Consider the following code:

#include <vector>

class A
{
public:    
    A(A&&);  // somewhat expensive

    static std::vector<A> make_As()
    {
        std::vector<A> result;
        result.push_back(A(3));
        result.push_back(A(4));
        return result;
    }

private:
    A(int);  // private constructor
};

Since A's move constructor is somewhat expensive (for whatever reason), I'd like to avoid calling it and use emplace_back() instead:

#include <vector>

class A
{
public:    
    A(A&&);  // somewhat expensive

    static std::vector<A> make_As()
    {
        std::vector<A> result;
        result.emplace_back(3);
        result.emplace_back(4);
        return result;
    }

private:
    A(int);  // private constructor
};

Unfortunately, with emplace_back(), the actual constructor call is done by something in the standard library, which is not privileged enough to be able to call A's private constructor.

I realize that there's probably little that can be done about this, but nonetheless I feel that since the calls to emplace_back() occur within a member of A, they ought to be able to call the private constructor.

Are there any workarounds for this?

The only thing I can think of is to add a friend-declaration to A, but the precise class that needs to be A's friend (that is, the class that actually tries to invoke the constructor) is implementation-specific (for example, for GCC it's __gnu_cxx::new_allocator<A>). EDIT: just realized that such a friend declaration will allow anyone to emplace_back() A's constructed with the private constructor into a container of A's, so it wouldn't really solve anything, I might as well make the constructor public at that point...

UPDATE: I should add that A's move constructor being expensive is not the only reason to avoid having to call it. It may be that A is not movable at all (nor copyable). That would not work with vector, of course, (because emplace_back() may have to reallocate the vector), but it would with deque, which also has a similar emplace_back() method but does not have to reallocate anything.

HighCommander4
  • 50,428
  • 24
  • 122
  • 194
  • Side comment, but move constructors should be the fastest form of construction...sounds like a design issue. – GManNickG Jul 11 '12 at 05:33
  • The side comment is incorrect. It is (never any slower and, except for other optimizations, usually) faster to construct an object than to construct-then-move an object, simply because it is doing less work. emplace_back constructs an object in place. – Nevin Jul 11 '12 at 05:59
  • 2
    @GManNickG: Using `push_back` will incur the cost of the private constructor + the cost of the move constructor. Using `emplace_back` will incur only the cost of the private constructor, so it would be strictly faster. – HighCommander4 Jul 11 '12 at 05:59
  • @HighCommander4: Yes, but I'm saying this: "Since `A`'s move constructor is somewhat expensive (for whatever reason)" should probably not be a concern. – GManNickG Jul 11 '12 at 06:00
  • 1
    @GManNickG: Plus, there are more reasons to avoid having to call the move constructor than to avoid its cost. For instance, A may not be move constructible at all (suppose I replace `vector` with `deque` - that would be a plausible scenario). I will edit the answer to mention this. – HighCommander4 Jul 11 '12 at 06:01
  • 2
    @GManNickG: Finally, there are reasons why a move constructor might have to be expensive. For example, consider a class that aggregates a large `std::array` member. – HighCommander4 Jul 11 '12 at 06:03
  • @HighCommander4: I'm not saying there are no circumstances where it matters, I'm saying in your case it *probably* doesn't matter. You're going to return this vector and each one is getting moved anyway, so if you care about performance (the basis of your post) you might want to improve your move speed. – GManNickG Jul 11 '12 at 06:12
  • 1
    @GManNickG: Returning a vector by value does **not** cause the move constructor of each of the elements to be called. The move constructor of the vector will be called, which will simply copy the vector's internal pointer/size member and not touch the elements at all. – HighCommander4 Jul 11 '12 at 06:13
  • @HighCommander4: Well duh, those are the kind of comments I make when I'm dead tired. Carry on. – GManNickG Jul 11 '12 at 06:26

3 Answers3

21

One possible workaround (or kludge) would be to use a helper class to hold the parameters to the private ctor of A (let's call this class EmplaceHelper). EmplaceHelper should also have a private ctor, and it should be in mutual friendship with A. Now all you need is a public ctor in A that takes this EmplaceHelper (via const-ref, probably), and use that with emplace_back(EmplaceHelper(...)).

Since EmplaceHelper can only be constructed by A, your public ctor is still effectively private.

It might even be possible to generalize this idea with a templated EmplaceHelper (possibly using std::tuple to hold the ctor parameters).

Edit: actually, it seems I overcomplicated this as a comment below from GManNickG gave me a simpler idea: add a private helper class (private_ctor_t in the example) that is just an empty class but since it is private it is only accessible by A. Modify A's constructor to include this private class as the first (or last) parameter (and make it public). The effect would be that only A could construct itself as if it had a private constructor, but this construction could be easily delegated now.

Like this:

#include <vector>
class A 
{ 
private:
    struct private_ctor_t {};

public:     
    A(private_ctor_t, int x) : A(x) // C++11 only, delegating constructor
    {}

    A(A&&) { /* somewhat expensive */ }

    static std::vector<A> make_As() 
    { 
        std::vector<A> result; 
        result.emplace_back(private_ctor_t{}, 3); 
        result.emplace_back(private_ctor_t{}, 4); 
        return result; 
    } 

private: 
    A(int) { /* private constructor */ }
}; 

If delegated constructors are not available, you can either factor out the common code for each version, or just get rid of A(int) altogether and only use the new version.

mitchnull
  • 6,161
  • 2
  • 31
  • 23
  • 2
    thanks GManNickG for polishing it :) I formulated my answer on the subway in the way in to work, couldn't flash out the example code then ;) – mitchnull Jul 11 '12 at 07:47
10

By the C++11 standard, all of the standard containers should use the allocator::construct method to do in-place construction. As such, you could simply make std::allocator a friend of A.

I suppose technically this function is allowed to delegate the actual construction call to something else. Personally, I think the spec should be a little more strict about enforcing exactly which objects call constructors and what can and cannot be delegated.

If such delegation occurs, or for whatever reason, you could provide your own allocator that forwards all calls to std::allocator except for construct. I don't suggest the latter, as many standard container implementations have special code for dealing with std::allocator that allows them to take up less space.

just realized that such a friend declaration will allow anyone to emplace_back() A's constructed with the private constructor into a container of A's, so it wouldn't really solve anything, I might as well make the constructor public at that point...

Then you're going to have to decide what's more important to you: in-place construction, or hiding privates. By it's very nature, in-place construction means that someone not in your code is doing the construction. Therefore, there's no way around it: some external code must be named a friend or the constructor must be public. In short, the constructor must be publicly accessible to whomever is delegated the construction.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • 4
    I agree with the sentiment that the method that does the construction should be specified given the non-transitive friendship we have in C++. – Matthieu M. Jul 11 '12 at 08:44
  • [My answer there](http://stackoverflow.com/a/25595837/1722660) may shed some light on this problem. It won't work with standard deque with standard allocator (need's to be subclassed), but prooves it can be done with custom allocator (but I had to override `rebind` as well). The accepted answer here is better for the requirements of the author, so, I won't place my solution here, just [the link](http://stackoverflow.com/a/25595837/1722660). – firda Sep 01 '14 at 08:38
2

Let's simplify a bit. The following fails to compile, because V has no access to A's private constructor.

struct V
{
    E(int i)
    {
        // ...
        auto a = A(i);
        // ...
    }
};

Going back to your code, V is just a simplification of vector, and V::E is just a simplification of what emplace_back does. vector doesn't have access to A's private constructor, and vector::emplace_back needs to call it.

Nevin
  • 4,595
  • 18
  • 24
  • I understand the mechanics of why it doesn't work. I'm just looking for a workaround. – HighCommander4 Jul 11 '12 at 06:02
  • 1
    @HighCommander4: Unfortunately there isn’t a good work around, since C++ `friend` is pretty ad hoc. You can make it public, as you’ve noted, but if you want it to be a little more explicit, like “hey I’m using this function we wanted private but couldn’t!”, you can do a trick like this: `struct foo { struct using_private_constructor {}; foo(int, const using_private_constructor&); };`, now the call site looks like: `foo f(5, foo::using_private_constructor())`, or `result.emplace_back(3, foo::using_private_constructor())`. Maybe you can justify just making it a normal public, though. – GManNickG Jul 11 '12 at 06:55