Reputation: 981
Given this example:
// 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_;
};
I would expect for an error to be raised around void setA(A* a) { a_ = *a; }
in B since A's assignment operator is made private and A and B aren't friends, but no error get's raised when I compile this.
$ g++ -c test.cpp // My invocation for reference
My question is why is this allowed? Is this behavior perhaps guaranteed according to the c++ standard?
I noticed that if I did not make B a templated class, I get the error as expected.
$ 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
^~~~~~~~
This led me to believe that because I wasn't actually "using" the templated B that the compiler could simply "ignore it" and optimize it out. I find this difficult to believe though since I'm not compiling with optimizations, and I still can't reproduce the error when I do use templated B.
// Appending this to test.cpp still doesn't result in an error.
void func(A &alloc) {
B<int> b(alloc);
b.func();
}
I can also confirm that for normal methods I get the error I expect. Changing void func();
in B to void func() { a_.func(); }
results in:
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();
^
I've confirmed all this behavior with clang++ (v6.0.1), g++ (v7.4.0), and tip of tree clang, and against -std=c++98
to -std=c++17
.
Upvotes: 1
Views: 105
Reputation: 385405
My question is why is this allowed?
It isn't.
But, since you never actually instantiated the template B<T>::setA()
, the code has not "finished compiling".
I find this difficult to believe though since I'm not compiling with optimizations
This has nothing to do with optimisations.
I still can't reproduce the error when I do use templated B.
You need to call setA()
on it.
You might consider this to be an extension of Substitution Failure Is Not An Error; I shall call it Not Using The Broken Template Thing Is Not An Error. Or NUTBTTINAE for short. :)
See also: instantiate a std::vector<T>
for some T
that is not copyable or moveable, and observe that it works until you try pushing things into it.
Upvotes: 3
Reputation: 52621
Non-virtual member functions of a class template - like setA
in your example - are not instantiated unless and until actually used.
Upvotes: 6