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

时间:2015-09-22 22:38:31

标签: c++ c++11

以下代码是我今天使用的简化版本。它有2个类A和B.B类尝试使用类A的私有构造函数,但它失败了。 如果我将构造函数设为public,则代码编译正常。为什么会这样?

#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;
}

我在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) {
    ^

5 个答案:

答案 0 :(得分:3)

你必须和朋友std::allocator<A>;,因为那是谁试图访问构造函数。

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

虽然从给定的示例中很难对整体设计发表评论,但我认为只是将其公开,因为任何能够看到class A的人都可以构建它,如果stl可以的话。

答案 1 :(得分:1)

A的构造函数的实际调用发生在std::allocator<A>::construct,而不在B的范围内,因此您需要std::allocator<A>成为朋友。但是,这将允许每个人调用您的私有构造函数,这可能是不可取的。

最简单的方法(不涉及使A的构造函数公开或有效公开)只需在A中构造B并将它们移动到向量中。 (您显示的A隐式声明了复制和移动构造函数。)

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

如果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);
}

(这种方法也可以更常用,例如,在没有友谊的语言中,例如Java。)

答案 2 :(得分:0)

错误clang输出实际上非常具有描述性。调用std::vector<A> a_vec(int_ptr_vec.begin(), int_ptr_vec.end());会调用一个可以实现(非常天真)的构造函数,如下所示:

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

由于类中所需的构造函数被标记为private,因此当通过编译器的模板传递扩展特定函数时,编译无法成功。

在这里标记构造函数是正确的( >毕竟是公开使用的。)

或者,您可以将构造函数保留为私有,并在创建后以其他方式手动填充该向量。编译不会失败,因为不需要实例化需要构造函数的模板代码。类似的东西:

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
}

答案 3 :(得分:0)

晚了聚会,但这是一个同事给我展示的技巧,可以通过一种简单的方法解决此问题:

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

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

然后从一个朋友类构造:

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

答案 4 :(得分:-1)

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