如果将赋值运算符设为私有,为什么仍允许此赋值?

时间:2019-08-14 01:01:16

标签: c++

给出以下示例:

// test.cpp
class A {
public:
private:
    A& operator=(const A&);  // don't allow assignment operator
    A(const A&);  // don't allow default copy constructor
    void func();
};

template<class T>
class B {
public:
    B(A &a) : a_(a) {}
    void setA(A* a) { a_ = *a; }  // No error is raised here.
    void func();

protected:
    B& operator=(const B&) { return *this; }
    A& a_;
};

我希望B中的void setA(A* a) { a_ = *a; }周围会出现错误,因为A的赋值运算符被设为私有,并且A和B不是朋友,但是在编译时不会出现错误。

$ g++ -c test.cpp  // My invocation for reference

我的问题是为什么允许这样做?是否可以根据c ++标准保证这种行为?

我注意到,如果我不将B用作模板化类,则会得到预期的错误。

$ g++ -c test.cpp
test.cpp: In member function ‘void B::setA(A*)’:
test.cpp:11:29: error: ‘A& A::operator=(const A&)’ is private within this context
     void setA(A* a) { a_ = *a; }
                             ^
test.cpp:4:8: note: declared private here
     A& operator=(const A&);  // don't allow assignment operator
        ^~~~~~~~

这使我相信,因为我实际上并没有“使用”模板化的B,所以编译器可以简单地“忽略它”并对其进行优化。我发现这很难让人相信,因为我没有进行优化,并且在使用模板化B时仍然无法重现错误。

// Appending this to test.cpp still doesn't result in an error.
void func(A &alloc) {
  B<int> b(alloc);
  b.func();
}

我还可以确认对于常规方法,我会得到预期的错误。将B中的void func();更改为void func() { a_.func(); }会导致:

test.cpp:14:22: error: 'func' is a private member of 'A'
    void func() { a_.func(); }
                     ^
test.cpp:6:10: note: declared private here
    void func();
         ^

我已经使用clang ++(v6.0.1),g ++(v7.4.0)和树形叮当提示并针对-std=c++98-std=c++17确认了所有这些行为。

2 个答案:

答案 0 :(得分:6)

类模板的非虚拟成员函数(例如您的示例中的setA)不会实例化,除非并且直到实际使用。

答案 1 :(得分:3)

  

我的问题是为什么允许这样做?

不是。

但是,由于您从未实际实例化模板safer_text,因此代码尚未“完成编译”。

  

我发现这很难相信,因为我没有进行优化

这与优化无关。

  

当我使用模板化B时,仍然无法重现错误。

您需要在其上拨打B<T>::setA()


您可能认为这是替换失败不是错误的扩展;我将其称为不使用损坏的模板,这不是错误。或简称为NUTBTTINAE。 :)


另请参见:为某个不可复制或不可移动的setA()实例化一个std::vector<T>,并观察到它起作用,直到您尝试将东西推入其中。