You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何V<std::unique_ptr<int>>的不可拷贝赋值断言失败?如何修复?

问题原因分析

1. U<T>断言成功的原因

U<T>中,当T为std::unique_ptr<int>(不可拷贝赋值)时:

  • 用户声明的模板版operator=因std::is_copy_assignable_v<T>为false被SFINAE禁用。
  • 编译器尝试生成默认拷贝赋值运算符,但由于成员t是std::unique_ptr<int>(其拷贝赋值运算符被显式删除),默认拷贝赋值运算符会被隐式删除。
  • 最终U<std::unique_ptr<int>>没有可用的拷贝赋值运算符,std::is_copy_assignable_v返回false,断言成功。

2. V<T>断言失败的原因

V<T>中,当T为std::unique_ptr<int>时:

  • 用户声明的模板版operator=同样被SFINAE禁用。
  • 由于模板版operator=不属于C++标准定义的“用户声明的拷贝赋值运算符”(必须是非模板成员函数),编译器会生成默认拷贝赋值运算符。
  • 成员v是std::vector<std::unique_ptr<int>>:在C++20之前,vector的拷贝赋值运算符签名存在(仅调用时会因元素不可拷贝而编译失败),因此编译器生成的V<T>默认拷贝赋值运算符不会被删除。
  • std::is_copy_assignable_v仅检查表达式t = u的签名匹配性(不深入函数体),会认为该表达式良构,返回true,导致断言失败。
修复方案

根据不同C++标准,有两种可行的修复方式:

方式一:C++11/17兼容方案

通过声明两个重载的非模板拷贝赋值运算符,结合SFINAE控制可用性:

#include <vector>
#include <memory>
#include <type_traits>

template<typename T>
class V {
public:
    // 当T可拷贝赋值时,提供有效的拷贝赋值运算符
    typename std::enable_if<std::is_copy_assignable_v<T>, V&>::type
    operator=(const V& rhs) {
        v = rhs.v;
        return *this;
    }

    // 当T不可拷贝赋值时,显式删除拷贝赋值运算符
    typename std::enable_if<!std::is_copy_assignable_v<T>, V&>::type
    operator=(const V& rhs) = delete;
private:
    std::vector<T> v;
};

template<typename T>
class U {
public:
    template<typename U = T, std::enable_if_t<std::is_copy_assignable_v<U>, int> = 0>
    auto operator = (const U &rhs) -> U & { t = rhs.t; return *this; }
private:
    T t;
};

int main()
{
    static_assert(!std::is_copy_assignable_v<std::unique_ptr<int>>);    // success
    static_assert(!std::is_copy_assignable_v<U<std::unique_ptr<int>>>); // success
    static_assert(!std::is_copy_assignable_v<V<std::unique_ptr<int>>>); // now success
    return 0;
}

方式二:C++20及以上方案

直接使用requires约束简化代码:

#include <vector>
#include <memory>
#include <type_traits>

template<typename T>
class V {
public:
    // 仅当T可拷贝赋值时,拷贝赋值运算符才可用
    V& operator=(const V& rhs) requires (std::is_copy_assignable_v<T>) {
        v = rhs.v;
        return *this;
    }
private:
    std::vector<T> v;
};

template<typename T>
class U {
public:
    template<typename U = T, std::enable_if_t<std::is_copy_assignable_v<U>, int> = 0>
    auto operator = (const U &rhs) -> U & { t = rhs.t; return *this; }
private:
    T t;
};

int main()
{
    static_assert(!std::is_copy_assignable_v<std::unique_ptr<int>>);    // success
    static_assert(!std::is_copy_assignable_v<U<std::unique_ptr<int>>>); // success
    static_assert(!std::is_copy_assignable_v<V<std::unique_ptr<int>>>); // now success
    return 0;
}

修复原理

两种方案均通过阻止编译器生成默认拷贝赋值运算符,并将拷贝赋值运算符的可用性与T的可拷贝赋值性绑定:

  • 当T不可拷贝赋值时,类的拷贝赋值运算符被显式删除或不存在,std::is_copy_assignable_v返回false,断言成功。
  • 当T可拷贝赋值时,拷贝赋值运算符正常可用。

内容的提问来源于stack exchange,提问作者slyx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 06:54:34