C++ 模板生成器的显式复制构造函数错误

C++ 模板生成器的显式复制构造函数错误,c++,porting,explicit,C++,Porting,Explicit,将旧代码从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

将旧代码从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 );
}
模板类TTT
{
公众:
模板布尔分配(P1、P2)
{
p=新的X(p1,p2);
返回true;
}
T*p;
};
结构AAA
{
虚拟~AAA(){}
虚拟bool dup(TTT&dst,bool param)const=0;//在所有派生类中都需要dup方法
};
结构BBB:公共AAA
{
显式BBB(常量BBB&src,布尔参数=false);
bool dup(TTT&dst,bool参数)常数覆盖;
};
内联布尔BBB::dup(TTT&dst,布尔参数)常数
{
返回dst.allocT(*此参数);
}
确切的错误消息是

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'
1>[…]:错误C2664:'bool TTT::allocT(P1,P2)':无法将参数1从'const BBB'转换为'BBB'
1> 与
1>          [
1> P1=BBB,
1> P2=布尔
1>          ]
1> […]:注意:结构“BBB”的构造函数声明为“explicit”
如果执行以下操作之一,此错误将消失:

  • 构造函数被声明为非显式的(正如编译器建议的)
  • 构造函数的“param”参数声明为非默认值:

    显式BBB(常量BBB&src,布尔参数)
    (尽管仍然是明确的)

  • 对allocT的调用是完全专门化的:

    返回dst.allocT(*此参数)

这些解决方案都不适合我:

  • 我不想删除显式,因为它看起来可疑——看起来编译器正在尝试创建一个临时文件并进一步传递它
  • 删除默认参数有效地防止构造函数成为副本构造函数,并可能生成编译器定义的版本,稍后用于创建临时构造函数
  • 每次指定所有构造函数参数类型并不方便。我只想把参数转发给BBB的构造函数
为了理解编译器为什么不能将
*this
赋值为
常量BBB&
,我创建了一个test helper函数,它显式地将指针转换为常量引用,这也没有帮助:

template const T&deref(const T*ptr){…}
...
返回dst.allocT(deref(this),param);

关于源代码的几个注释:

  • TTT是一种智能指针,它提供了非标准特性,不能用标准智能指针替代
  • AAA和BBB实际上是相当重的引用计数类,因此复制它们是非常不理想的

在代码移植中这是一个很难预料的问题,我在这里完全感到困惑。似乎我错过了现代C++标准中的一些东西,它阻止了旧代码在新编译器下编译。我试图在这里找到一个解决方案,但我不能,如果它是重复的,很抱歉。请帮助我解决问题,或者至少了解问题的根源。

当您将
BBB
传递给
allot
函数时,您执行了一次意外复制。这会导致编译器错误

您正在此处调用分配函数:

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( *this, param );
}
或者在将
BBB
传递给
allot
时显式复制它

inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( BBB(*this), param );
}
内联布尔BBB::dup(TTT&dst,布尔参数)常量
{
返回dst.allocT(BBB(*此),参数);
}
或者(从C++17开始)

内联布尔BBB::dup(TTT&dst,布尔参数)常量
{
返回dst.allocT(静态强制转换(*此),参数);
}

VS2003和VS2017中指定的C++标准版本是什么?我没有指定MS/S转换2017,所以MCSS 2003的答案可能是C++ 98,MSV 2017的答案是C++ 14。是的,这甚至可以创建两个重载,即:<代码> P1<代码>和<代码> P1 const和< /C>第一个参数。不确定它是否会在其他地方造成一些损害)有趣的后果:现在需要为复制构造函数指定
explicit
。事实上,这很好,我不想看到以这种方式复制的对象(重对象)的隐藏和意外复制操作。谢谢你,Timo,现在看起来好多了)但是我仍然不明白编译器为什么抱怨,以及实际的问题是什么。但是为什么P1不是
BBB常量&
?我传递了
*这个
,它可以表示为
const BBB
BBB const&
,所以为什么不选择合适的类型并将其转发给构造函数呢?因为解析为引用引用的类型,有效地删除了cv限定符和引用<代码>常量BBB&
将解析为
BBB
(编译器已在错误消息中告知)。引用cppreference:“如果P是cv限定类型,则在扣除时忽略顶级cv限定符。”以及“如果P是引用类型,则P引用的类型用于扣除。”
template<typename X, typename P1, typename P2> bool 
allocT( P1 const& p1, P2 p2 ) { ... }
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( BBB(*this), param );
}
inline bool BBB::dup( TTT<AAA> & dst, bool param ) const
{
    return dst.allocT<BBB>( static_cast<BBB>(*this), param );
}