4

使用 Visual C++ 2010,我有一个这样的类:

class MyClass{
public:
    MyClass(){}
    MyClass(MyClass &){/*...*/}   //A
    MyClass(const MyClass &){/*...*/}   //B
    template<typename T> MyClass(T &&t){ static_assert(
        !std::is_same<typename 
        std::remove_cv<typename  std::remove_reference<T>::type>::type, 
        MyClass>::value,
        "Wrapping over wrapping is not allowed!"); } //C
};

int main(int, char**){
    MyClass a;
    const MyClass b(a); //assert fail if line A removed
    auto c=b; //assert fail if line B removed
}
//If both A and B exists
//Warning C4521: multiple copy constructors specified
//Furthermore, if both A and B removed
//No error or warnings; VC2010 accepts peacefully.
//In debug mode you will find the compiler generated trivial copy constructor

根据 C++ 标准,A 行和 B 行都被认为是复制构造函数,而 C 是转换构造函数。我收到一条警告说我声明了多个复制构造函数,这并不奇怪。但是,如果我删除其中任何一个,static_assert 将失败并且代码将无法编译,这意味着模板构造函数获得了控制权。

我确信这种行为遵循函数重载的规则。然而,这是两个规则的冲突吗?如果 A 和 B 是复制构造函数并且声明了其中一个,那么任何复制对象的尝试都不应该放到模板中,对吗?

更新:根据 N3242, 12.8.7,

“成员函数模板永远不会被实例化来执行将类对象复制到其类类型的对象。”

正确的实现应该是:

  • 当 A 或 B 或两者都被移除时,不应发生断言失败。
  • 如果删除了行 B,则 c 的构造应该会失败,因为 b 是 const。
  • 如果这两行都被删除,编译器应该为该类生成一个复制构造函数。
  • 如果两条线都存在,则由实现来警告用户。

任何意见?

4

2 回答 2

1

首先,template<T>应该是template <typename T>

我在 64 位 Ubuntu Linux 上使用 gcc 4.4.3,代码的行为与您在帖子中演示的不同。

  • 如果没有任何改变,则可以在没有任何警告的情况下编译代码。构造函数 A 和 B 依次被调用。
  • 如果我评论 A 行,它就无法像您所说的那样编译:failed in line const MyClass b(a);。原因是对象a不是常量,构造函数B无法匹配,编译器必须实例化模板构造函数。当然,const MyClass而且MyClass是不同的类型。
  • 但是,如果我只注释 B 行,则可以成功编译代码并调用模板复制构造函数。对象 b 是一个常量对象,所以构造 A 无法匹配,编译器实例化模板构造函数。但是,问题仍然存在:static_assert 是否会失败?差异可能是由于平台/编译器的差异。GCC 似乎实现了is_same<MyClass&&, MyClass>::value真实。您可以使用 typeid 打印出这两种类型。
于 2011-10-25T06:55:40.513 回答
0

如果 A 和 B 是复制构造函数并且声明了其中一个,那么任何复制对象的尝试都不应该放到模板中,对吗?

不是复制构造函数的构造函数仍可用于复制对象。在您的情况下,从构造函数模板实例化的构造函数用于复制对象。这很好。

于 2011-10-25T11:00:38.860 回答