std::make_unique invoking private constructor from friend class [duplicate] - c++

I mostly work on system-level C++ projects that don't allow exceptions to be thrown, but RAII is (rightfully) strongly encouraged. Right now, we handle the lack of failing constructors using infamous tricks many C++ programmers are familiar with, like:
Trivial constructor followed by a call to bool init(Args...) to do the hard stuff
Real constructor followed by checking bool is_valid() const
Heap-allocating with static unique_ptr<MyType> create(Args...)
Of course, these all have drawbacks (heap allocation, invalid and "moved" states, etc).
My company is finally updating compilers and will allow glorious C++17 to be used. Since C++17 features std::optional<T> and, most importantly, mandatory copy elision, I was hoping I could greatly simplify all our classes into something that would look like this:
class MyType {
public:
static std::optional<MyType> create() {
// If any of the hard stuff fails, return std::nullopt
return std::optional<MyType>(std::in_place, 5, 'c');
}
~MyType() {
// Cleanup mArg0 and mArg1, which are always valid if the object exists
}
// ... class functionality ...
// Disable default constructor, move, and copy.
// None of these are needed because mandatory copy elision
// allows the static function above to return rvalue without
// copy or move operations
MyType() = delete;
MyType(const MyType&) = delete;
MyType(MyType&&) = delete;
MyType& operator=(const MyType&) = delete;
MyType& operator=(MyType&&) = delete;
private:
MyType(ArgT0 arg0, ArgT1 arg1) : mArg0(arg0), mArg1(arg1) {}
ArgT0 mArg0;
ArgT1 mArg1;
};
Notice how nice this is: Static function ensures all the hard stuff is done before the object is ever created, lack of default ctor/move means object never exists in an invalid or moved state, private constructor ensures user can't accidentally skip the named ctor.
Unfortunately, because the ctor is private, the std::is_constructable_t<MyType> check fails and therefore the in_place constructor of optional is SFINAE'd out.
This code works if I do one of 2 things, neither of which I want to:
Make the ctor public (But now users of the class can accidentally circumvent the named ctor)
Allow the move operations (But now I have to deal with invalidated objects)
I have also tried this, but it doesn't work because std::optional required a move operator for this to work:
static std::optional<MyType> create() {
// If any of the hard stuff fails, return std::nullopt
return std::optional<MyType>(MyType(5, 'c'));
}
Is there some trick or incantation I may be missing to get this to work, or have I hit the limits of what C++17 will allow?
Thanks!

If you want to make any indirect object construction work (emplace in its various forms, in_place constructors of optional, make_shared, etc) , the constructor in question must be public. You can make a constructor public without allowing all public use by using something called a private key.
Basically, you create a type (call it Key) whose default constructor is private. The class has no members, nor does it do anything. It declares that MyType is a friend of Key; this means that only members of MyType can construct one.
Now, make all of MyType's constructors public, but they all take a Key const& as the first parameter. This means that in theory anyone could call them, but in practice only someone who has a Key instance can actually call them. Members of MyType can create such an instance, and they can pass those instances to optional's in_place constructor or any other indirect mechanism. This effectively gives the indirect construction mechanism private access to the constructor.
This is a standard idiom for dealing with forwarding of private access to a type. Indeed, one could hypothetically write a generic key<T> type like this:
template<typename T>
class key
{
private:
key() = default;
key(int) {} //Not an aggregate
friend T;
};
One small note. Because of an annoyance of C++11 pre-C++20, any type with no members and no constructors other than defaulted/deleted copy/move/default constructors is considered an aggregate. This is true even if you explicitly = default its default constructor. As such, that type can undergo aggregate initialization, which has no public/private distinction. That is, anybody could call your private-key constructors by doing this: MyType({}, <params>);.
To avoid this, you will need to give Key an additional (private) constructor or otherwise prevent it from being an aggregate.

Related

How to properly apply rule of 5 (or zero?) to a class containing a vector of custom objects with strings

I'm having trouble wrapping my brain around ownership and maximizing performance with moves. Imagine this hypothetical set of classes emulating an Excel workbook.
namespace Excel {
class Cell
{
public:
// ctors
Cell() = default;
Cell(std::string val) : m_val(val) {};
// because I have a custom constructor, I assume I need to also
// define copy constructors, move constructors, and a destructor.
// If I don't my understanding is that the private string member
// will always be copied instead of moved when Cell is replicated
// (due to expansion of any vector in which it is stored)? Or will
// it be copied, anyways (so it doesn't matter or I could just
// define them as default)
value() const { return m_val; }; // getter (no setter)
private:
std::string m_val;
}
class Row
{
public:
// ctors
Row() = default;
Row(int cellCountHint) : m_rowData(cellCountHint) {}
// copy ctors (presumably defaults will copy private vector member)
Row(const Row&) = default;
Row& operator=(Row const&) = default;
// move ctors (presumably defaults will move private vector member)
Row(Row&& rhs) = default;
Row& operator=(Row&& rhs) = default;
// and if I want to append to internal vector, might I get performance
// gains by moving in lieu of copying, since Cells contain strings of
// arbitrary length/size?
void append(Cell cell) { m_rowData.push_back(cell); };
void append(Cell &&cell) { m_rowData.push_back(std::move(cell)); };
private:
std::vector<Cell> m_rowData;
}
}
And so on:
A Worksheet class would contain a vector of Rows
A Workbook class would contain a vector of Worksheets
I felt no need to implement these last two for the MWE since they are effectively duplicative of Row (my assumption is they would be identical to the design of Row).
It's confusing to me to understand if it's ok to rely on the defaults or if I should be defining my own move constructors (and not keeping them default) to ensure the private vector member variable is moved rather than copied, but this is all very confusing to me and I can only seem to find overly simplistic examples of a class with nothing but members of built-in types.
If a class deals with the ownership of a resource, then that class should ONLY manage that resource. It shouldn't do anything else. In this case define all 5 (rule of 5).
Else, a class doesn't need to implement any of the 5 (rule of 0).
Simple as that.
Now, I've seen these rules formulated as follows: if a class defines any of the 5 then it should define all of them. Well, yes, and no. The reason behind it is: if a class defines any of the 5 then that is a strong indicator that the class has to manage a resource, in which case it should define all 5. So, for instance if you define a destructor to free a resource then the class is in the first category and should implement all 5, but if you define a destructor just to add some debug statements or do some logging, or because the class is polymorphic then the class is not the first category, so you don't need to define all 5.
If your class is in the second category and you define at least one of the 5, then you should explicitly =default the rest of the 5. That is because the rules on when cpy/move ctors/assignments are implicitly declared are a bit complicated. E.g. defining a dtor prevents the implicit declaration of move ctor & assigment
// because I have a custom constructor, I assume I need to also
// define copy constructors, move constructors, and a destructor.
Wrong. A custom constructor is not part of the 5.
All of your classes should follow the rule of 0, as neither of them manages resources.
Actually it's pretty rare for a user code to need to implement a rule of 5 class. Usually that's implemented in a library. So, as a user, almost always for to the rule of 0.
The default copy/move ctors/assignments do the expected thing: the copy ones will copy each member, and the move ones will move each member. Well, in the presence of members that aren't movable, or have only some of the 5, or have some of the 5 deleted, then the rules go a little more complex, but there are no unexpected behaviors. The default are good.

How to prohibit the construction of object?

How can I prohibit the construction of an object? I mark = delete; all relevant special functions as follows:
struct A
{
A() = delete;
A(A const &) = delete;
A(A &&) = delete;
void * operator new(std::size_t) = delete;
void operator delete(void *) = delete;
};
A x{};
A y = {};
A * z = ::new A{};
LIVE EXAMPLE
But x, y and *z can still exist. What to do? I am interested in both cases; static/stack allocation and heap allocation.
One option would be to give the class a pure virtual function, and mark it final:
struct A final
{
virtual void nonconstructible() = 0;
};
[Live example]
If you want to have just static members, then write namespace A rather than struct A. Ensuing code will be syntactically similar.
To prevent creation of an instance of a class, make it abstract. (Include one pure virtual function). But doing this introduces a v-table into you class, which you might not want.
If you want to make it impossible to instantiate the class you could just declare private constructors:
class NotInstantiable {
private:
NotInstatiable();
public:
};
And not defining NotInstantiable further. This can't now be instantiated since first the constructor is private but also that a definition for the constructor has not been provided.
The second obstacle for instantiate the NotInstantiable would for example prohibit this possibility, which in fact otherwise is a well known pattern:
class NotInstantiable {
private:
NotInstantiable();
public:
NotInstantiable* evil_method()
{
return new NotInstantiable(); // this will fail if there's no body of the constructor.
}
};
In general, to completely prevent client code instantiation of a class you can declare the class final and either
make the constructors non-public, or
delete the constructors and make sure that the class isn't an aggregate, or
add a pure virtual member function (e.g. make the destructor pure virtual) to make the class abstract.
Declaring the class final is necessary when the non-public is protected, and for the abstract class, in order to prevent instantiation of a base class sub-object of a derived class.
To partially prohibit instantiation, you can
make the destructor non-public.
This prevents automatic and static variables, but it does not prevent dynamic allocation with new.
make the class' allocation function (the operator new) non-public.
This prevents dynamic allocation via an ordinary new-expression in client code, but it does not provide automatic and static variables, or sub-objects of other objects, and it does not prevent dynamic allocation via a ::new-expression, which uses the global allocation function.
There are also other relevant techniques, such as an allocation function with extra arguments that make new-expressions inordinately complicated and impractical. I used that once to force the use of a special macro to dynamically allocate objects, e.g. for a shared-from-this class. But that was in the time before C++11 support for forwarding of arguments; nowadays an ordinary function can do the job, and such a function can be made a friend of the class.
The fact that the code compiles with at least one version of the clang compiler with -std=gnu++1z, is due to a bug and/or language extension in that compiler.
The code should not compile, since it invokes the default constructor that has been deleted. And it does not compile with e.g. MinGW g++ 5.1.0, even with -std=gnu++1z.
The fact that the code compiles with at least one version of the clang compiler with -std=gnu++1z, may be due to a bug and/or language extension in that compiler. What the correct behavior is, is unclear because
Although the code compiles with clang and with Visual C++ 2015, it does not compile with e.g. MinGW g++ 5.1.0, even with -std=gnu++1z.
Intuitively the delete would be meaningless if the code should compile, but many meaningless constructs are permitted in C++.
At issue is whether the class is an aggregate (in which case the new expression performs aggregate initialization), which rests on whether the deleted default constructor can be regarded as user-provided. And as user TartanLlama explains in comments, the requirements for user-provided are
C++11 §8.4.2/4
” A special member function is user-provided if it is user-declared and not explicitly
defaulted or deleted on its first declaration.
I.e. although the delete of the default constructor in this question's example declares that constructor, it's not user-provided (and ditto for the other members) and so the class is an aggregate.
The only defect report I can find about this wording is DR 1355, which however just concerns an issue with the use of the words “special member”, and proposes to drop those words. But, considering both the effect demonstrated by this question, and considering that a function can only be deleted on its first declaration, the wording is strange.
Summing up, formally, as of C++11 (I haven't checked C++14), the code should compile. But this may be a defect in the standard, with the wording not reflecting the intent. And since MinGW g++ 5.1.0 doesn't compile the code, as of October 2015 it's not a good idea to rely on the code compiling.
Essentially this compiles and is allowed because the type A is an aggregate type and the aggregate initialisation doesn't use default constructors.
What is an aggregate type?;
class type (typically, struct or union), that has
no private or protected members
no user-provided constructors (explicitly defaulted or deleted constructors are allowed) (since C++11)
no base classes
no virtual member functions
Giving it any one of the above would make it non-aggregate and thus the aggregate initialisation would not apply. Giving it a private user defined (and unimplemented) constructor will do.
struct A
{
A() = delete;
A(A const &) = delete;
A(A &&) = delete;
void * operator new(std::size_t) = delete;
void operator delete(void *) = delete;
private:
A(int);
};
As a side note; I hope this is a defect in the language specifications. At first look I thought that this should not compile, yet it does. One of the motivations for the =delete was to avoid the C++03 "trick" of declaring the constructors private to "hide" them and thus be unusable. I would expect a =delete on the default constructor to effectively prohibit class creation (outside other user defined constructors).
For easier reading and clearer intent, consider even an empty base class;
struct NonAggregate{};
struct A : private NonAggregate
{
//...
Maybe the simplest yet is to return to the C++03 style here, make the default constructor private;
struct A
{
private:
A(); // note no =delete...
};

Why default move ctor and assignment are no more added by compiler when a destructor is defined?

I cannot understand the rationale behind the automatic addition of default ctors. In particular I find very awkward that every single time I just need to add an empty virtual destructor and nothing more, I loose the move stuffs, but adding them I loose the copy and default things, so I end up adding all this chunk of code:
virtual ~SomeClass(){} // you are the guilty!
//virtual ~SomeClass() = default // would be the same
SomeClass(SomeClass&&) = default; // no more auto-added
SomeClass& operator=(SomeClass&&) = default; // no more auto-added
SomeClass(const SomeClass&) = default; // but with the moves defined,
SomeClass& operator=(const SomeClass&) = default; // I'm now missing the copy
SomeClass(){} // and the default as well
I'm sure there is a reason for making my classes ugly and letting me desire an evil macro, I just would like to know it to feel more comfortable.
Take a look at this. It explains something called the rule of five, which is essentially what standard requires.
Generally, for most cases, compiler creates defaults for copy constructor, copy assignment, move assignment, and destructor. But, if a programmer defines any of these, then the compiler assumes the user has encapsulated something in this class that requires his/her special, let's say. destructor. Now that the programmer knows that he/she is going to need a destructor, the compiler will know that the programmer know what's going on and just not create the defaults for the rest (because, based on the assumption that the compiler makes, the default ones are going to be wrong, and can even result in undesired behavior).
The problem is that your class is trying to do two separate things: providing a polymorphic interface (hence the need for the virtual destructor) and managing concrete data members (hence the need for the copy/move operations). It's generally a good idea to give each class a single responsibility.
I'd move the virtual destructor, and any virtual function declarations, to an empty, abstract base class. Then any concrete class(es) deriving from that will be free to autogenerate all the needful things.
Example:
#include <iostream>
struct Movable {
Movable() {}
Movable(Movable&&m) {std::cout << "Moving\n";}
};
struct SomeInterface {
virtual ~SomeInterface() {}
// no data members, so no need for any other special member functions
};
struct SomeClass : SomeInterface {
Movable stuff;
// no user-declared special functions, so all are auto-generated
};
int main() {
SomeClass c;
SomeClass c2(std::move(c)); // uses the auto-generated move constructor
}

Private constructor inhibits use of emplace[_back]() to avoid a move

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.
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.
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.
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.

Is RVO allowed when a copy constructor is private and not implemented?

Suppose I have a class where the copy constructor is private and not implemented (to make the object non-copyable)
class NonCopyable {
// whatever
private:
NonCopyable( const NonCopyable&);
void operator=(const NonCopyable&);
};
Now in some member function of the same class I write code that returns an object of that class:
NonCopyable NonCopyable::Something()
{
return NonCopyable();
}
which is a case when RVO could kick in.
RVO still requires that a copy constructor is accessible. Since the possible call to the copy constructor is done from within the same class member function the copy constructor is accessible. So technically RVO is possible despite the fact that the intent was to prohibit using the copy constructor.
Is RVO allowed in such cases?
Yes, RVO would be allowed in this case - at least if the caller of Something() was a class member or friend.
I think this is one reason why private inheritance of a non-copyable class is better than doing it 'manually' in each class you want to prevent copying in. In that case there's no accidental loophole.
For example, using boost::noncopyable:
class NonCopyable : private boost::noncopyable {
public:
NonCopyable() {};
NonCopyable Something();
};
NonCopyable NonCopyable::Something()
{
return NonCopyable(); // causes compile time error, not link time error
}
Your example is quite interesting.
This is the typical C++03 declaration.
class NC {
public:
NC NC::Something() {
return NC();
}
private:
NC(NC const&);
NC& operator=(NC const&);
};
Here, as noted, RVO may kick in even though we semantically wanted to avoid copying.
In C++03, the solution is to delegate:
class NC: boost::noncopyable {
public:
NC NC::Something() { // Error: no copy constructor
return NC();
}
};
In C++11, we have the alternative of using the delete keyword:
class NC {
public:
NC NC::Something() { // Error: deleted copy constructor
return NC();
}
private:
NC(NC const&) = delete;
NC& operator=(NC const&) = delete;
};
But sometimes, we want to prevent copy, but would like to allow Builder (as in the Pattern).
In this case, your example works as long as RVO kicks in, which is a bit annoying as it is, in essence, non-standard. A definition of the copy constructor should be provided but you wish it not to be used.
In C++11, this usecase is supported by deleting copy operations and defining move operations (even privately).
It is allowed, but you may get a link error because the copy constructor is not implemented.
If you provide a body for NonCopyable( const NonCopyable&), it would work.
One important point may overshadow the actual question.
What is the use case of such function if RVO is allowed ?
This function can be called in 3 ways and 2 of them will be a compiler error:
NonCopyable obj;
NonCopyable obj2 = obj; // 1 --> error
NonCopyable &r = obj; // 2 --> error
const NonCopyable &rc = obj; // 3 --> ok, but dangerous (may lead to UB)
One can't use the returned object effectively, so it really doesn't matter if the RVO is allowed or not.