My code needs a special constructor that std::construct_at
can use. This works fine if it's public but then it can be accessed by users of the class. If it is private, std::construct_at
fails. Here's the minimal code:
#include <memory>
#include <iostream>
class Vec {
public:
int* const p{};
const int len{};
Vec(int len = 1) : len(len), p(new int[len]{}) {}
// Raw ctor, for use only inside class using std::construct_at to overlay object
// Doesn't work executed from std::construct_at if private
Vec(int* p, int len) : p(p), len(len) {}
Vec& operator=(Vec&& rhs)
{
delete[] p;
std::construct_at(this, rhs.p, rhs.len); // dtor not needed for this class
std::construct_at(&rhs, nullptr, 0); // steal rhs guts and make valid
return *this;
}
// .... Other ctors
~Vec() {delete[] p;}
};
int main()
{
int* pi{};
Vec x(pi, 0); // ctor accessable to users. Not good.
Vec a, b(2);
b.p[1] = 42;
a = std::move(b);
std::cout << "a.p[1] " << a.p[1] << " b.p (Null) " << b.p << "\n";
}
So I tried creating an empty struct tag (INNER) defined in the private space, but used in the public ctor. This works:
#include <memory>
#include <iostream>
class Vec {
struct INNER {};
public:
int* const p{};
const int len{};
Vec(int len = 1) : len(len), p(new int[len] {}) {}
// Raw ctor, for use only inside class using std::construct_at to overlay object
// Doesn't work executed from std::construct_at if private
Vec(int* p, int len, struct INNER) : p(p), len(len) {}
Vec& operator=(Vec&& rhs)
{
delete[] p;
std::construct_at(this, rhs.p, rhs.len, INNER{}); // dtor not needed for this class
std::construct_at(&rhs, nullptr, 0, INNER{}); // steal rhs guts and make valid
return *this;
}
// .... Other ctors
~Vec() { delete[] p; }
};
int main()
{
int* pi{};
//Vec x(pi, 0, Vec::INNER{}); // not compilable since INNER is private
Vec a, b(2);
b.p[1] = 42;
a = std::move(b);
std::cout << "a.p[1] " << a.p[1] << " b.p (Null) " << b.p << "\n";
}
While this works in GCC, CLANG, and MSVC. But it seems odd that std::construct_at
can use it since INNER is defined in the private segment. Is std::construct_at
s use of INNER legit?
CodePudding user response:
There's nothing wrong about it. When you define a INNER
class in the private scope of an OUTER
class, the only thing you are not allowed is manually using the name of INNER
class outside of of OUTER
.
Assuming you have a class like:
class OUTER {
class INNER {};
public:
INNER create_inner() { return INNER{}; }
};
So you are not allowed to do:
int main() {
OUTER::INNER inner{};
}
However, you can always do:
int main() {
auto inner = OUTER{}.create_inner();
}
Note:
In your code, while user are not allowed to do Vec x(pi, 0, Vec::INNER{})
anymore, they are free to use it like Vec x(pi, 0, {})
.
CodePudding user response:
But it seems odd that
std::construct_at
can use it sinceINNER
is defined in theprivate
segment. Isstd::construct_at
s use ofINNER
legit?
std::construct_at
by itself only use public interface. it is legit.
passkey idiom has some caveat though, and you are misusing it.
private
forbids to use the name explicitly, but you can use other form (as implicit contructor with {/*..*/}
or decltype
, ...) to actually use the thing which should be protected.
You have to protect a little more:
class Vec
{
class Key
{
friend Vec; // Who can have the key
constexpr KeyT() {} // Not default to avoid non contrainted uniform initialization call (< C 20)
constexpr KeyT(const KeyT&) = default;
};
public:
// ...
// "private", except for those which have the key
constexpr Vec(int* p, int len, Key) : p(p), len(len) {}
Vec& operator=(Vec&& rhs)
{
std::destroy_at(this);
std::construct_at(this, rhs.p, rhs.len, Key{});
std::construct_at(&rhs, nullptr, 0, Key{});
return *this;
}
// .... Other ctors
~Vec() { delete[] p; }
};
Here, only Vec
can create a Key
, and it can pass it to any functions