mdod
mdod

Reputation: 23

explicit copy constructor for template generator error

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:

None of these solutions suits me:

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:

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.

Upvotes: 2

Views: 182

Answers (1)

Timo
Timo

Reputation: 9825

You perform an accidental copy of BBB when you pass it to your allocT function. That causes the compiler error.

You are calling the allocation function here:

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( *this, param );
}

Because your allocation function takes its arguments as values, they will be copied:

template<typename X, typename P1, typename P2> 
bool allocT( P1 p1, P2 p2 ) { ... }

However this is an implicit (hidden) copy, because you don't explicitly specify to copy the object (this in your case). Since you declared your copy constructor explicit these copy invocations aren't allowed anymore.

To fix it you can either change allocT to take a reference (which I recommend)

template<typename X, typename P1, typename P2> bool 
allocT( P1 const& p1, P2 p2 ) { ... }

or you explicitly copy your BBB when passing it to allocT like

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( BBB(*this), param );
}

or (since C++17)

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( static_cast<BBB>(*this), param );
}

Upvotes: 4

Related Questions