简体   繁体   中英

friend class cannot call private constructor

Code below is a simplified version of something I was working with today. It has 2 classes A and B. Class B tries to use a private constructor of class A, but it fails. If I make the constructor public the code compiles fine. Why is that?

#include <vector>

class A
{
friend class B;
private:
    A(int * int_ptr) {
        m_int = *int_ptr;
    }
private:
    int m_int;
};


class B
{
friend class A;
public:
    static void create_vec_a() {
        int v1(1);
        int v2(2);

        std::vector<int *> int_ptr_vec{
            &v1,
            &v2
        };

        std::vector<A> a_vec(int_ptr_vec.begin(),
            int_ptr_vec.end());
    }
};

int  main(int argc, char *argv[])
{
    B::create_vec_a();
    return 0;
}

Error I get in Visual Studio is:

'A::A': cannot access private member declared in class 'A'

On Clang:

test.cpp:28:18: note: in instantiation of function template specialization 'std::vector<A, std::allocator<A> >::vector<__gnu_cxx::__normal_iterator<int **, std::vector<int *, std::allocator<int *> > >, void>' requested here
            std::vector<A> a_vec(int_ptr_vec.begin(),
                           ^
test.cpp:7:2: note: declared private here
    A(int * int_ptr) {
    ^

You'd have to friend std::allocator<A>; , because that's who's trying to access the constructor.

class A
{
    friend class std::allocator<A>;
...

It's hard to comment on the overall design though from the given sample, but I'd consider just making it public because anyone who can see the class A can construct it if the stl can.

The actual call to the constructor of A occurs in std::allocator<A>::construct , not within the scope of B , so you would need std::allocator<A> to be a friend. However, this would allow everyone to call your private constructor, which is probably undesirable.

The easiest approach (which does not involve making A 's constructor public or effectively public) would just be to construct the A 's within B and move them into the vector. (The A you've shown has implicitly declared copy and move constructors.)

std::vector<A> a_vec;
for (int* p : int_ptr_vec) {
    a_vec.push_back(A(p));
}

If A is not meant to be movable (rare, but it can happen if it contains, say, an atomic variable or a mutex as a member) then you can consider more sophisticated access control techniques, such as requiring a special token to be passed to the constructor.

class A
{
public:
    class Token
    {
        friend class B;
    private:
        Token() {}
    };
    A(Token, int * int_ptr) {
        m_int = *int_ptr;
    }
private:
    int m_int;
};

// ...
// inside B
std::vector<A> a_vec;
for (int* p : int_ptr_vec) {
    a_vec.emplace_back(A::Token(), p);
}

(This approach can also be used more generally, eg, in languages without friendship, such as Java.)

The error clang outputs is actually very descriptive. The call std::vector<A> a_vec(int_ptr_vec.begin(), int_ptr_vec.end()); would call a constructor that could be implemented (very naively) as something that looks like this:

vector(TIterator _Begin, TIterator _End) {
  for (auto it = _Begin; it < _End; ++it) {
    this->push_back(T(*it)); // This calls the constructor you have marked as private
  }
}

Since the required constructor in your class is marked private, there's no way for the compilation to succeed when that particular function is expanded by the template pass of your compiler.

Marking that constructor public is the right thing to do here (it is publically used after all).

Alternately, you could leave the constructor private and manually populate the vector in some other way after its creation. Compilation would not fail because the template code that requires the constructor wouldn't be instantiated. Something like:

std::vector<A> a_vec;
for (auto ptr: int_ptr_vec) {
  a_vec.push_back(A::create(ptr)); // Define create() in A as you wish
}

Late to the party, but here's a trick a colleague showed me that solves this problem in a simple way:

class Foo
{
    // This is used to make the constructor effectively private
    struct PrivateConstruct
    {
    };

public:
    Foo(MyType myArg, PrivateConstruct);
...
};

Then to construct from a friend class:

auto uniqueFoo = std::make_unique<Foo>(myValue, Foo::PrivateConstruct());

尝试在 B 类中使用朋友 A::A(int * int_ptr)。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM