Consider this example:
a.h
#pragma once
#include <memory>
struct AAA;
std::shared_ptr<AAA> createAAA();
a.cpp
#include "A.h"
struct AAA : public std::enable_shared_from_this<AAA>{};
void destory_AAA(AAA* v) {
v->~AAA();
}
std::shared_ptr<AAA> createAAA() {
auto ret = std::make_shared<AAA>();
// check if shared_from_this is OK after make_shared
auto shared_from_ths = ret->shared_from_this(); // #1
return shared_from_ths;
}
main.cpp
#include "A.h"
void destory_AAA(AAA* v);
std::shared_ptr<AAA> unused_func(AAA* ptr) {
return std::shared_ptr<AAA>{ ptr, destory_AAA }; // #2
}
int main()
{
createAAA();
}
In this example, under the debug mode of certain MSVC, #1
throws an error bad weak_ptr
. Some guys say the ODR rule causes this problem [basic.def.odr] p15. However, I think the template definition of std::shared_ptr
does not violate those rules.
What is the rule in the C++ standard that interprets the reason of the problem? I suspect the rule is [temp.res.general] p6:
The validity of a templated entity may be checked prior to any instantiation. The program is ill-formed, no diagnostic required, if:
- […]
- the interpretation of such a construct in the hypothetical instantiation is different from the interpretation of the corresponding construct in any actual instantiation of the templated entity.
Both #1
and #2
will cause the following templated function instantiated
template <class _Ux>
void _Set_ptr_rep_and_enable_shared(_Ux* const _Px, _Ref_count_base* const _Rx) noexcept { // take ownership of _Px
this->_Ptr = _Px;
this->_Rep = _Rx;
if constexpr (conjunction_v<negation<is_array<_Ty>>, negation<is_volatile<_Ux>>, _Can_enable_shared<_Ux>>) {
if (_Px && _Px->_Wptr.expired()) {
_Px->_Wptr = shared_ptr<remove_cv_t<_Ux>>(*this, const_cast<remove_cv_t<_Ux>*>(_Px));
}
}
}
However, in the TU main.cpp
, the struct AAA
is incomplete while it is complete in the a.cpp
. Is this the correct rule that interprets the problem? Is ODR rule relevant here?