为什么在MSVC12中unique_ptr的is_copy_constructible返回true

11

我本以为这个静态断言会触发:

#include <type_traits>
#include <memory>

int main() {
  static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "UPtr has copy constructor?");
}

但它并没有。

使用MSVC12编译:

Microsoft (R) C/C++优化编译器版本18.00.31101适用于x64


1
奇怪,在GCC 5(Linux)上,断言触发了。 - hlt
1
它在使用g++编译时失败:http://coliru.stacked-crooked.com/a/d0592d5e7824f2cd - NathanOliver
在我所知道的范围内,webcompiler是相对最新的Visual Studio版本,但该程序无法正常运行。 - Shafik Yaghmour
好的,那就是我们版本中的编译器bug了!不幸的是,我们不能仅仅升级它。虽然可以轻松解决,但这似乎很奇怪。 - sji
5
@RichardHodges,谢谢你关心,但我很喜欢我的工作环境。在你侮辱别人的工作场所之前,请考虑现在不升级可能有充分的理由。 - sji
还有相关内容:https://dev59.com/4Ybca4cB1Zd3GeqPUVIC - stgatilov
2个回答

15
static_assert 应该会触发,因为 std::unique_ptr 有一个隐式删除的复制构造函数,因此这是一个 bug。这似乎与这个 bug 报告 std::is_copy_constructible is broken 有关:

(1) std::is_copy_constructible 返回 true,对于带有已删除复制构造函数的类型。

(2) std::is_copy_constructible 返回 true,对于组成不可复制类型的类型。

回复如下:

感谢您报告此错误。我们已经修复了它,并且修复将在 2013 年后的 Visual Studio 的下一个主要版本中提供。

还请查看此 bug 报告:std::is_copy_constructible doesn't work correctly
请注意,在使用最新版 Visual Studio 的 webcompiler 上,assert 触发。最后更新时间为 Dec 3, 2015。assert 还会在 clang(see it live) 和 gcc 上触发。
我找到了一个 bug 报告:A strange behavior of std::is_copy_constructible,其中有与您的代码非常相似的代码。
static_assert(std::is_copy_constructible<std::unique_ptr<int>>::value, "");

回复如下:

感谢您报告此错误。我们已经修复了它,并且在VS 2015 Preview中提供了修复程序。

不清楚这个问题是在哪个版本的Visual Studio中修复的。有一个回复说是2013年后期版本,而后面的回复则说是2015年预览版。


不知道那个在线编译器,这将是一个很方便的工具,谢谢。 - sji
"std::unique_ptr既不是可复制构造,也不是可复制分配的" - 让我们不要将CopyConstructible和CopyAssignable(概念)与is_copy_constructible等检查混淆。前者比后者强得多。 - T.C.
@T.C. 我明白那可能会让人感到困惑,已经修复了。 - Shafik Yaghmour

0

以下是四种使类不可复制的方法:

#include <stdio.h>
#include <type_traits>

class A {
public:
    A(const A&) = delete;
    void operator=(const A&) = delete;
};

class B {
private:
    B(const B&) = delete;
    void operator=(const B&) = delete;
};

class C {
public:
    C(const C&) = delete;
    void operator=(const C&) = delete;
    void operator=(C) = delete;
};

class D {
private:
    D(const D&) = delete;
    void operator=(const D&) = delete;
    void operator=(D) = delete;
};

int main() {
    printf("%d %d\n", std::is_copy_constructible<A>::value, std::is_copy_assignable<A>::value);
    printf("%d %d\n", std::is_copy_constructible<B>::value, std::is_copy_assignable<B>::value);
    printf("%d %d\n", std::is_copy_constructible<C>::value, std::is_copy_assignable<C>::value);
    printf("%d %d\n", std::is_copy_constructible<D>::value, std::is_copy_assignable<D>::value);
}

在 MSVC2013 x64 (18.00.40629 for x64) 上,它会打印出:
1 1    //A
0 1    //B
1 0    //C
0 0    //D

在一个合适的编译器上,所有八个值都必须是零。

不幸的是,这并不能为解决MSVC2013中的错误提供一个好的方法,即使对于你自己的类也是如此。因为如果你声明接受值参数的赋值运算符,那么你不能在同一类中声明移动赋值运算符(任何移动赋值都无法编译,因为存在歧义重载)。

P.S. 修复赋值的关键思想来自于this related answer


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接