简体   繁体   English

友元类不能调用私有构造函数

[英]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.它有 2 个类 A 和 B。 B 类尝试使用 A 类的私有构造函数,但失败了。 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:我在 Visual Studio 中得到的错误是:

'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>;你必须和std::allocator<A>;朋友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.尽管从给定的示例中很难对整体设计发表评论,但我会考虑将其公开,因为如果 stl 可以,任何可以看到class A都可以构造它。

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.A的构造函数的实际调用发生在std::allocator<A>::construct ,不在B的范围内,因此您需要std::allocator<A>成为朋友。 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.最简单的方法(不涉及使A的构造函数公开或有效公开)只是在B构造A并将它们移动到向量中。 (The A you've shown has implicitly declared copy and move constructors.) (您显示的A隐式声明了复制和移动构造函数。)

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.如果A不是可移动的(很少见,但如果它包含原子变量或互斥量作为成员,则可能会发生),那么您可以考虑更复杂的访问控制技术,例如需要传递特殊令牌到构造函数。

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.) (这种方法也可以更普遍地使用,例如,在没有友谊的语言中,如 Java。)

The error clang outputs is actually very descriptive.错误 clang 输出实际上非常具有描述性。 The call std::vector<A> a_vec(int_ptr_vec.begin(), int_ptr_vec.end());调用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).将构造函数标记为 public 是正确的做法(毕竟它公开使用的)。

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)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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