Being porting old code from MSVS2003 to MSVS2017 and ran into problems. The following code (an excerpt) is compiled fine under MSVS2003 and fails under MSVS2017:
template<typename T> class TTT
{
public:
template<typename X, typename P1, typename P2> bool allocT( P1 p1, P2 p2 )
{
p = new X( p1, p2 );
return true;
}
T * p;
};
struct AAA
{
virtual ~AAA(){}
virtual bool dup( TTT<AAA> & dst, bool param ) const = 0; // require dup method in all derived classes
};
struct BBB : public AAA
{
explicit BBB( const BBB & src, bool param = false );
bool dup( TTT<AAA> & dst, bool param ) const override;
};
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( *this, param );
}
The exact error message is
1>[...]: error C2664: 'bool TTT<AAA>::allocT<BBB,BBB,bool>(P1,P2)': cannot convert argument 1 from 'const BBB' to 'BBB'
1> with
1> [
1> P1=BBB,
1> P2=bool
1> ]
1> [...]: note: Constructor for struct 'BBB' is declared 'explicit'
This error disappears if one of following is done:
- constructor is declared non-
explicit
(as the compiler advises); the `param' parameter of constructor is declared non-default:
explicit BBB( const BBB & src, bool param );
(remaining explicit though);the call to allocT is fully specialized:
return dst.allocT< BBB, const BBB &, bool >( *this, param );
None of these solutions suits me:
- I wouldn't like to remove
explicit
since it looks suspicious -- it looks like the compiler is trying to create a temporary and pass it further; - the removal of default parameter effectively prevents the constructor of being a copy-constructor and probably generates a compiler-defined version that is used later for creating a temporary;
- it's not handy to specify all of constructor parameter types each time. I just want to forward parameters to the constructor of BBB.
Trying to understand why the compiler cannot assign *this
into const BBB &
, I created a test helper function which explicitly converts a pointer into const reference and this didn't help either:
template const T & deref( const T * ptr ) { ... }
...
return dst.allocT( deref(this), param );
Several notes on the source code:
- TTT is a kind of smart pointer, it cannot be replaced with standard smart pointers as it provides non-standard features;
- AAA and BBB are in fact quite heavy classes that are reference counted and thus copying them is very non-optimal.
It's very unexpectable problem in code porting, I'm totally puzzled here. Seems that I missed something in modern C++ standard that prevents old code of being compiled under new compilers. I tried to find a solution here on SO but I couldn't, sorry if it's a duplicate. Please help me to solve it or at least understand the roots of the problem.