可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I was halfway through working on this piece of code and thought this is obviously not going to compile before hitting the build button. I was surprised that it not only compiled, but linked and worked as well.
If I were to guess I would say that SFINAE is responsible for it compiling... is it?
struct BaseClass
{
public:
BaseClass() {}
template<typename T>
BaseClass(const T& a_other)
{
int i = 0; // for break point
}
template<typename T>
BaseClass& operator= (const T& a_other)
{
int i = 0; // for break point
return *this;
}
private:
BaseClass(const BaseClass& a_other); // Does not have a definition
BaseClass& operator= (const BaseClass& a_other); // Does not have a definition
};
struct MyClass : public BaseClass
{
};
int main()
{
MyClass i, j;
i = j;
return 0;
}
EDIT: I am using Visual-C++ 2008, maybe it is an odd quirk of VS
回答1:
The code is not legal.
i = j
calls the implicitly defined copy assignment operator in MyClass
. This function calls the copy assignment operator for each of its sub-objects, including direct base classes [class.copy 12.8 p28].
If you add code to the copy assignment operator for BaseClass you can see where VS is going wrong:
template<typename T>
BaseClass& operator= (const T& a_other)
{
std::cout << typeid(T).name() << '\n';
int i = 0; // for break point
return *this;
}
For me this prints out "struct MyClass". VS is calling the BaseClass
copy assignment operator by passing the parameter received in MyClass:operator=
directly, rather than just the BaseClass
sub object of j.
SFINAE doesn't come into play because the template functions aren't failing. VS is simply generating the implicit copy assignment operator incorrectly.
To sum up:
VS is generating the implicit copy assignment operator as
MyClass &operator=(const MyClass& rhs) {
static_cast<BaseClass&>(*this).operator=(rhs);
return *this;
}
When it should be:
MyClass &operator=(const MyClass& rhs) {
static_cast<BaseClass&>(*this).operator=(static_cast<const BaseClass&>(rhs));
return *this;
}
回答2:
Shot in the dark: the compiler instantiates the base class’ operator =
with T
= MyClass
. I do now know whether this is legal or even required but it makes a certain kind of sense: the auto-generated code for operator =
essentially looks like this (well, pseudo-code):
MyClass& operator =(MyClass const& other) {
BaseClass::operator =(other);
return *this;
}
Now the compiler finds that BaseClass::operator =<MyClass>(MyClass const&)
is the best match and instantiates it.
回答3:
Well, since it cannot call BaseClass::operator=(const BaseClass&)
(from the default generated MyClass::operator=
for what it's worth) because it's private, it just calls template<typename T> BaseClass::operator(const T &)
with T=BaseClass
. So there is no call to a non-defined function.
I guess the situation would be different if the others were public, in which case the compiler would prefer those over the template, but since he cannot see them when being private, the template versions match equally well.
回答4:
The compiler will auto generate a (default) copy constructor for MyClass
as one is not defined. If you change i
and j
's type from MyClass
to BaseClass
you will see the error you expected, as the compiler then tries to bind the private, unimplemented assignment operator.
Going into this a little deeper using MSVC 2010 Ultimate SP1, we can see the exact reason:
MyClass::operator=:
00201230 push ebp
00201231 mov ebp,esp
00201233 push ecx
00201234 mov dword ptr [ebp-4],ecx
00201237 mov eax,dword ptr [__that]
0020123A push eax
0020123B mov ecx,dword ptr [this]
0020123E call BaseClass::operator=<MyClass> (202130h)
00201243 mov eax,dword ptr [this]
00201246 mov esp,ebp
00201248 pop ebp
00201249 ret 4
the assignment operator is called for MyClass
by BaseClass::=<T>
using MyClass
as the type through MyClass
es default copy operator, whether this is a bug or an MSVC-ism is up to MS and the C++ standard to decide.
回答5:
MyClass
will use an implicitly-defined copy assignment operator, since there's no user-declared version. Ac cording to the standard, that implicit copy-assignment will perform the following (C++03 12.8/13 "Copying class objects"):
- Each subobject is assigned in the manner appropriate to its type:
— if the subobject is of class type, the copy assignment operator for the class is used (as if by explicit qualification; that is, ignoring any possible virtual overriding functions in more derived classes);
Note that in 12.8/9 the standard defines a user-declared copy assignment operator as " a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&" (emphasis mine).
So according to the standard, the template function
template<typename T>
BaseClass& operator= (const T& a_other);
Should not be called by the MyClass
implicit copy assignment operator. MSVC is acting in a non-standard fashion here. GCC rightly diagnoses this:
C:\temp\test.cpp: In member function 'MyClass& MyClass::operator=(const MyClass&)':
C:\temp\test.cpp:29:14: error: 'BaseClass& BaseClass::operator=(const BaseClass&)' is private
C:\temp\test.cpp:33:8: error: within this context
C:\temp\test.cpp: In function 'int main()':
C:\temp\test.cpp:40:7: note: synthesized method 'MyClass& MyClass::operator=(const MyClass&)' first required here