【问题标题】:templated constructor vs. templated copy constructor模板化构造函数与模板化复制构造函数
【发布时间】:2011-05-24 02:13:40
【问题描述】:

我有一个带有模板构造函数的类,用于隐式移动转换,但是这个构造函数不应该用于该类(它应该只能是可复制构造的)。但是,编译器总是尝试使用模板化构造函数而不是常规的复制构造函数。

例如有了这个,我得到了以下编译器错误,link。 (如果您想尝试,可以复制粘贴此代码)

struct implementation{};
class my_class
{
 my_class(my_class&&); // delete move-constructor... OUCH... COMPILER ERROR
public:
 my_class(){}
 my_class(const my_class& other) : impl_(other.impl_){}

 template<typename T>
 my_class(T&& impl) : impl_(std::make_shared<T>(std::move(impl))){} // Still tries to use this...

private:
 std::shared_ptr<implementation> impl_;
};

class other_class
{
public:
 my_class foo()
 { 
       return instance_; // Wants to use move-constructor???
 }
private:
 my_class instance_;
};

有人知道如何正确解决这个问题吗?

【问题讨论】:

  • 你有参考,要参考吗? o_O
  • 称为右值引用,见artima.com/cppsource/rvalue.html
  • 是的,&amp;&amp; 现在的意思是“逻辑和”。 “右值引用”,取决于上下文。令牌回收不是很棒吗? :)
  • 这不是 C++0x 的特性吗?我在标签中没有看到 C++0x。
  • @Pawel:好点,已编辑标签。

标签: c++ templates constructor c++11 copy-constructor


【解决方案1】:

好的,这是我对my_class 的全面改造:

class my_class
{
public:
    my_class() {}
    my_class(my_class&& that) : impl_(std::move(that.impl_)) {}

    template <typename T> my_class(T&& impl,
    typename std::enable_if<
        std::is_base_of<
            implementation,
            typename std::remove_reference<T>::type
        >::value,
        void
    >::type* dummy = 0
    ) : impl_(std::make_shared<T>(std::forward<T>(impl))) {}

    template <typename T>
    typename std::enable_if<
        std::is_base_of<
            implementation,
            typename std::remove_reference<T>::type
        >::value,
        my_class&
    >::type
    operator=(T&& impl)
    {
        std::make_shared<implementation>(std::forward<T>(impl)).swap(impl_);
        return *this;
    }

private:
    std::shared_ptr<implementation> impl_;
};

正如其他人所建议的,这适用于左值和右值引用,方法是使用 std::forward 而不是 std::moveremove_reference 是必需的,因为对于左值引用,T 是一个引用,derived&amp; 不是从 base 派生的,但 derived 是(注意引用)。

【讨论】:

  • @FredOverflow:隐式转换似乎不适用于您的修复,请参阅我的编辑。有什么想法吗?
  • @ronag:从错误信息看来,您似乎需要提供一个移动赋值运算符。
  • @FredOverflow:我试过了。也试过 foo_ = my_class(std::move(boo));在这种情况下,我得到“错误 C2440:'':无法从 'derived_1' 转换为 'my_class'。没有构造函数可以采用源类型,或者构造函数重载决议不明确”。
  • @ronag:我已经使用移动赋值运算符模板更新了我的答案。现在我需要睡觉了:)
  • @ronag:看起来很危险。请记住,T&amp;&amp; 也可以是左值引用,从它们中移出并不是一个好主意。此外,您最后缺少return *this;
【解决方案2】:

这永远行不通:

template<typename T>
my_class(typename std::enable_if<std::is_base_of<implementation, derived_1>::value, T&&>::type impl) : impl_(std::make_shared<T>(std::move(impl)))  {}

template <typename T> 
my_class& operator= (typename std::enable_if<std::is_rvalue_reference<T&&>::value && !std::is_same<T, my_class>::value, T&&>::type impl)

原因是您只在非推断上下文中使用T。简而言之,如果编译器应该从中推断出T 的参数具有Anything&lt;T&gt;::type 的形式,则编译器无法推断出T

所以,如果你想在赋值运算符上使用enable_if,你把它放在返回值中:

template <class T>
typename enable_if<..., T&>::type operator=(const T&);

在转换(也应该适用于移动)构造函数的情况下,您添加一个具有默认值的虚拟参数:

template <class T>
MyClass(const T&, typename enable_if<..., void>::type* =0);

FredOverflow 已经为您提供了正确的赋值运算符。

顺便说一句,如果您使用std::forward 而不是std::move,则无需将其限制为右值引用。见here。这会给你(从 FredOverflow 复制和粘贴):

template <typename T>
typename std::enable_if<std::is_base_of<implementation, T>::value, my_class&>::type
operator=(T&& impl)
{
    std::make_shared<implementation>(std::forward<T>(impl)).swap(impl_);
    return *this;
}

【讨论】:

    猜你喜欢
    • 2016-11-27
    • 1970-01-01
    • 2016-09-12
    • 1970-01-01
    • 2011-05-08
    • 2016-12-04
    • 2011-02-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多