模板生成器错误的显式复制构造函数
explicit copy constructor for template generator error
将旧代码从 MSVS2003 移植到 MSVS2017 并 运行 遇到问题。以下代码(摘录)在 MSVS2003 下编译正常,在 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 );
}
确切的错误信息是
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'
如果执行以下操作之一,此错误就会消失:
- 构造函数被声明为非
explicit
(如编译器建议的那样);
构造函数的 `param' 参数被声明为非默认值:
explicit BBB( const BBB & src, bool param );
(虽然仍然明确);
对 allocT 的调用是完全专用的:
return dst.allocT< BBB, const BBB &, bool >( *this, param );
None 这些解决方案适合我:
- 我不想删除
explicit
,因为它看起来很可疑 -- 看起来编译器正在尝试创建一个临时文件并进一步传递它;
- 删除默认参数有效地防止构造函数成为复制构造函数,并可能生成编译器定义的版本,稍后用于创建临时参数;
- 每次都指定所有构造函数参数类型并不方便。我只是想将参数转发给BBB的构造函数。
为了理解为什么编译器不能将 *this
分配给 const BBB &
,我创建了一个测试辅助函数,它显式地将指针转换为 const 引用,但这也没有帮助:
template const T & deref( const T * ptr ) { ... }
...
return dst.allocT( deref(this), param );
源码的几点说明:
- TTT是一种智能指针,不能用标准智能指针替代,因为它提供了非标准的特性;
- AAA 和 BBB 实际上相当重 类,它们是引用计数的,因此复制它们不是最佳选择。
这是代码移植中非常意外的问题,我在这里完全不解。似乎我错过了现代 C++ 标准中的某些内容,这些内容阻止旧代码在新编译器下进行编译。我试图在 SO 上找到解决方案,但我不能,如果它是重复的,我很抱歉。请帮助我解决它或至少了解问题的根源。
当您将 BBB
传递给您的 allocT
函数时,您执行了一个意外的副本。这会导致编译器错误。
您在这里调用分配函数:
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( *this, param );
}
因为您的分配函数将其参数作为值,它们将被复制:
template<typename X, typename P1, typename P2>
bool allocT( P1 p1, P2 p2 ) { ... }
然而,这是一个隐式(隐藏)副本,因为您没有明确指定要复制对象(this
在您的情况下)。由于您声明了复制构造函数 explicit
,因此不再允许这些复制调用。
要修复它,您可以更改 allocT
以获取参考(我推荐)
template<typename X, typename P1, typename P2> bool
allocT( P1 const& p1, P2 p2 ) { ... }
或者您在将 BBB
传递给 allocT
时明确复制
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( BBB(*this), param );
}
或(C++17 起)
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( static_cast<BBB>(*this), param );
}
将旧代码从 MSVS2003 移植到 MSVS2017 并 运行 遇到问题。以下代码(摘录)在 MSVS2003 下编译正常,在 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 );
}
确切的错误信息是
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'
如果执行以下操作之一,此错误就会消失:
- 构造函数被声明为非
explicit
(如编译器建议的那样); 构造函数的 `param' 参数被声明为非默认值:
explicit BBB( const BBB & src, bool param );
(虽然仍然明确);对 allocT 的调用是完全专用的:
return dst.allocT< BBB, const BBB &, bool >( *this, param );
None 这些解决方案适合我:
- 我不想删除
explicit
,因为它看起来很可疑 -- 看起来编译器正在尝试创建一个临时文件并进一步传递它; - 删除默认参数有效地防止构造函数成为复制构造函数,并可能生成编译器定义的版本,稍后用于创建临时参数;
- 每次都指定所有构造函数参数类型并不方便。我只是想将参数转发给BBB的构造函数。
为了理解为什么编译器不能将 *this
分配给 const BBB &
,我创建了一个测试辅助函数,它显式地将指针转换为 const 引用,但这也没有帮助:
template const T & deref( const T * ptr ) { ... }
...
return dst.allocT( deref(this), param );
源码的几点说明:
- TTT是一种智能指针,不能用标准智能指针替代,因为它提供了非标准的特性;
- AAA 和 BBB 实际上相当重 类,它们是引用计数的,因此复制它们不是最佳选择。
这是代码移植中非常意外的问题,我在这里完全不解。似乎我错过了现代 C++ 标准中的某些内容,这些内容阻止旧代码在新编译器下进行编译。我试图在 SO 上找到解决方案,但我不能,如果它是重复的,我很抱歉。请帮助我解决它或至少了解问题的根源。
当您将 BBB
传递给您的 allocT
函数时,您执行了一个意外的副本。这会导致编译器错误。
您在这里调用分配函数:
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( *this, param );
}
因为您的分配函数将其参数作为值,它们将被复制:
template<typename X, typename P1, typename P2>
bool allocT( P1 p1, P2 p2 ) { ... }
然而,这是一个隐式(隐藏)副本,因为您没有明确指定要复制对象(this
在您的情况下)。由于您声明了复制构造函数 explicit
,因此不再允许这些复制调用。
要修复它,您可以更改 allocT
以获取参考(我推荐)
template<typename X, typename P1, typename P2> bool
allocT( P1 const& p1, P2 p2 ) { ... }
或者您在将 BBB
传递给 allocT
时明确复制
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( BBB(*this), param );
}
或(C++17 起)
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
return dst.allocT<BBB>( static_cast<BBB>(*this), param );
}