模板化特殊成员的重载分辨率(operator =)

时间:2018-05-27 10:44:00

标签: c++ c++11

上下文:尝试在模板化的自制shared_ptr上实现copy and swap惯用法。我无法调用此函数:

    template<typename U>
    shared_ptr<T>& operator=(shared_ptr<U> rhs)
    {
        static_assert(std::is_base_of<T, U>::value);
        swap(*this, rhs);
        return *this;
    }

从不必要的代码中剥离代码:

以下代码无法编译。 &#34; x = y&#34; line是尝试使用:

    foo<T>& operator=(const foo<U>& ptr) = delete;

如果我删除/评论已删除的功能行。意思是如果我没有定义它们,编译器将为我做,代码将编译。但是,编译器生成的特殊成员将优先于我的运算符,而不会被调用。

template<typename T> 
class foo
{
public:

    // copy&swap
    template<typename U>
    foo<T>& operator=(foo<U> rhs)
    {
        // whatever is needed swap and all
        return *this;
    }

    template<typename U>
    foo<T>& operator=(const foo<U>& ptr) = delete;
    template<typename U>
    foo<T>& operator=(foo<U>&& ptr) = delete;

    foo<T>& operator=(foo<T>&& ptr) = delete;
};

int main()
{
    foo<int> x,y;
    x = y;
    return 0;
}

问题

  • 这里我不清楚重载决议。为什么一个签名优先于另一个?
  • 如果该功能被删除,我理解它不是&#34;替换失败&#34;,但仍然是签名级别的失败,而不是在正文内部,为什么编译器停止寻找匹配?
  • 任何可以调用函数的解决方案?

修改/应答

到目前为止,这是正确的版本:

  • 非模板构造函数(移动/复制)被标记为显式,因此将foo分配给foo会调用模板版本。
  • 添加适当的静态断言,因此转换U / T有意义
  • 参数完美转发到分配/移动内部函数

代码:

#include <iostream>
template<typename T>
class foo
{
private:
    template <typename U> friend class foo;

    template<typename U>
    void assign(const foo<U>& other)
    {
        static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
        std::cout << "templated assign function" << std::endl;
        this->data = other.data;
    }

    template<typename U>
    void move(foo<U>&& other)
    {
        static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
        std::cout << "templated move function" << std::endl;
        this->swap(*this, other);
    }
public:

    template<class X, class Y> void swap(foo<X>& left, foo<Y>& right) noexcept
    {
        std::cout << "templated swap function" << std::endl;
        std::swap(left.data, right.data);
    }

    void swap(foo<T>& left, foo<T>& right) noexcept
    {
        std::cout << "swap function" << std::endl;
        std::swap(left.data, right.data);
    }

    foo() {}

    explicit foo(foo<T>&& other)
    {
        std::cout << "move constructor foo(foo&& other)" << std::endl;
        move(std::forward<decltype(other)>(other));
    }


    explicit foo(const foo<T>& other)
    {
        std::cout << "copy constructor foo(const foo& other)" << std::endl;
        assign(std::forward<decltype(other)>(other));
    }

    template<typename U>
    foo(foo<U>&& other)
    {
        static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);
        std::cout << "templated move constructor template<typename U> foo(foo<U>&& other)" << std::endl;
        move(std::forward<decltype(other)>(other));
    }

    template<typename U>
    foo(const foo<U>& other)
    {
        static_assert(std::is_base_of<T, U>::value || std::is_convertible<U,T>::value);
        std::cout << "templated copy constructor template<typename U> foo(const foo<U>& other)" << std::endl;
        assign(std::forward<decltype(other)>(other));
    }

    // copy&swap
    template<typename U>
    foo<T>& operator=(foo<U> rhs)
    {
        static_assert(std::is_base_of<T, U>::value || std::is_convertible<U, T>::value);

        std::cout << "templated assignement template<typename U> foo<T>& operator=(foo<U> rhs)" << std::endl;

        auto tmp = rhs.data;
        rhs.data = reinterpret_cast<U*>(data);
        data = reinterpret_cast<T*>(tmp);

        return *this;
    }

    foo<T>& operator=(foo<T> rhs)
    {
        std::cout << "assignement foo<T>& operator=(foo<T> rhs)" << std::endl;

        std::swap(rhs.data,data);
        return *this;
    }

private:
    T * data;
};

int main()
{
    foo<int> x,y;
    const foo<int>& cy = y;

    foo<short> z, w;
    x = y;
    x = cy;
    x = std::move(y);
    x = z;
    x = std::move(w);
    return 0;
}

1 个答案:

答案 0 :(得分:1)

  1. operator=(const foo&)仅在cv限定符中有所不同,并且将首选来自模板
  2. = delete导致operator=(const foo&)被禁止。它不仅阻止编译器生成它。
  3. 这应该这样做:

    #include <tuple>
    #include <iostream>
    
    template<typename T>
    class foo
    {
    private:
        template <typename U> friend class foo;
    
        foo& assign(T rhs)
        {
            std::swap(data, rhs);
            return *this;
        }
    
    public:
    
    
        template<typename U>
        foo& operator=(const foo<U>& rhs)
        {
            return assign(rhs.data);
        }
    
        template<typename U>
        foo& operator=(foo<U>&& rhs)
        {
            return assign(std::move(rhs.data));
        }
    
        foo& operator=(const foo& rhs)
        {
            return assign(rhs.data);
        }
    
        foo& operator=(foo&& rhs)
        {
            return assign(std::move(rhs.data));
        }
    
    private:
        T data;
    };
    
    int main()
    {
        foo<int> x,y;
        const foo<int>& cy = y;
    
        foo<short> z, w;
        x = y;
        x = cy;
        x = std::move(y);
        x = z;
        x = std::move(w);
        return 0;
    }
    

    编辑1:添加了成员​​并致电交换。

    修改2:更改了分配,以按值T取代foo<T>添加移动分配

    注意:在分配T不同类型(foo)时,现在使用U的转换构造函数