草庐IT

c++ - 带有复制/移动赋值运算符的 enable_if

coder 2023-11-13 原文

我有一个类,我想在其中启用复制/移动赋值运算符,仅当该类的类型参数分别不可抛出复制/移动构造时。所以我尝试这样做:

#include <type_traits>

template<typename T>
struct Foobar {

    Foobar(T value) : x(value) {}
    Foobar(const Foobar &other) : x(other.x) {}
    Foobar(Foobar &&other) : x(std::move(other.x)) {}

    template<bool Condition = std::is_nothrow_copy_constructible<T>::value,
             typename = typename std::enable_if<Condition>::type>
    Foobar &operator=(const Foobar &rhs) {
        x = rhs.x;
        return *this;
    }

    template<bool Condition = std::is_nothrow_move_constructible<T>::value,
             typename = typename std::enable_if<Condition>::type>
    Foobar &operator=(Foobar &&rhs) {
        x = std::move(rhs.x);
        return *this;
    }

    T x;
};

int main() {
    Foobar<int> foo(10);
    Foobar<int> bar(20);

    foo = bar;
    foo.operator=(bar);

    return 0;
}

现在,Clang 给我以下错误:

enable_if_test.cpp:31:9: error: object of type 'Foobar<int>' cannot be assigned because its copy assignment operator is implicitly
      deleted
    foo = bar;
        ^
enable_if_test.cpp:8:5: note: copy assignment operator is implicitly deleted because 'Foobar<int>' has a user-declared move
      constructor
    Foobar(Foobar &&other) : x(std::move(other.x)) {}
    ^
enable_if_test.cpp:32:9: error: call to deleted member function 'operator='
    foo.operator=(bar);
    ~~~~^~~~~~~~~
enable_if_test.cpp:4:8: note: candidate function (the implicit copy assignment operator) has been implicitly deleted
struct Foobar {
       ^
enable_if_test.cpp:12:13: note: candidate function [with Condition = true, $1 = void]
    Foobar &operator=(const Foobar &rhs) {
            ^
enable_if_test.cpp:19:13: note: candidate function [with Condition = true, $1 = void] not viable: no known conversion from
      'Foobar<int>' to 'Foobar<int> &&' for 1st argument
    Foobar &operator=(Foobar &&rhs) {
            ^
2 errors generated.

现在,我包括了对赋值运算符的显式调用,以展示错误的奇怪之处。它只是说我的模板是一个候选函数,但没有给出未被选中的原因。

我的猜测是,由于我定义了移动构造函数,编译器隐式删除了复制赋值运算符。由于它被隐式删除,它甚至不想实例化任何模板。然而,我不知道为什么它会这样。

我怎样才能实现这种行为?

(注意:实际代码有正当理由定义每个成员,我知道它在这里没用。)

最佳答案

最好也是唯一的方法是通过零规则——使用编译器提供的赋值运算符和构造函数,它们复制或移动每个成员。如果成员 T x 不能被复制(移动)赋值,那么你的类的复制(移动)赋值运算符将默认为 deleted。

SFINAE 不能用于禁用复制和/或移动赋值运算符的原因是 SFINAE 需要模板上下文,但复制和移动赋值运算符是非模板成员函数。

A user-declared copy assignment operator X::operator= is a non-static non-template member function of class X with exactly one parameter of type X, X&, const X&, volatile X& or const volatile X&.

由于您的模板版本不算作用户声明的复制(移动)赋值运算符,因此它们不会抑制默认值的生成,并且由于首选非模板,因此默认值将优先于您的模板定义(当参数是 const Foobar& 时,否则模板是更好的匹配,但禁用模板仍然不会禁用自动生成的模板)。

如果除了调用成员的复制(移动)赋值运算符之外还需要一些特殊的逻辑,在子对象中实现(基或成员都可行)。


您也许可以通过从用作基类的类模板的特化中进行选择,在继承时传递适当的类型特征来实现您的目标:

template<bool allow_copy_assign, bool allow_move_assign>
struct AssignmentEnabler;

template<typename T>
struct Foobar : AssignmentEnabler<std::is_nothrow_copy_constructible<T>::value, 
                                  std::is_nothrow_move_constructible<T>::value>
{
};

The derived type will use the rule of zero to default to having copy and move assignment if and only if the selected AssignmentEnabler base class does.您必须为四种组合(既不复制也不移动,复制不移动,移动不复制,两者)中的每一个组合专门化 AssignmentEnabler

完成问题中代码的转换:

#include <type_traits>

template<bool enable>
struct CopyAssignmentEnabler {};

template<>
struct CopyAssignmentEnabler<false>
{
    CopyAssignmentEnabler() = default;
    CopyAssignmentEnabler(const CopyAssignmentEnabler&) = default;
    CopyAssignmentEnabler(CopyAssignmentEnabler&&) = default;
    CopyAssignmentEnabler& operator=(const CopyAssignmentEnabler&) = delete;
    CopyAssignmentEnabler& operator=(CopyAssignmentEnabler&&) = default;
};

template<bool enable>
struct MoveAssignmentEnabler {};

template<>
struct MoveAssignmentEnabler<false>
{
    MoveAssignmentEnabler() = default;
    MoveAssignmentEnabler(const MoveAssignmentEnabler&) = default;
    MoveAssignmentEnabler(MoveAssignmentEnabler&&) = default;
    MoveAssignmentEnabler& operator=(const MoveAssignmentEnabler&) = default;
    MoveAssignmentEnabler& operator=(MoveAssignmentEnabler&&) = delete;
};

template<typename T>
struct Foobar : CopyAssignmentEnabler<std::is_nothrow_copy_constructible<T>::value>,
                MoveAssignmentEnabler<std::is_nothrow_move_constructible<T>::value>
{
    Foobar(T value) : x(value) {}

    T x;
};

int main() {
    Foobar<int> foo(10);
    Foobar<int> bar(20);

    foo = bar;
    foo.operator=(bar);

    return 0;
}

关于c++ - 带有复制/移动赋值运算符的 enable_if,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29687967/

有关c++ - 带有复制/移动赋值运算符的 enable_if的更多相关文章

  1. ruby - 多次弹出/移动 ruby​​ 数组 - 2

    我的代码目前看起来像这样numbers=[1,2,3,4,5]defpop_threepop=[]3.times{pop有没有办法在一行中完成pop_three方法中的内容?我基本上想做类似numbers.slice(0,3)的事情,但要删除切片中的数组项。嗯...嗯,我想我刚刚意识到我可以试试slice! 最佳答案 是numbers.pop(3)或者numbers.shift(3)如果你想要另一边。 关于ruby-多次弹出/移动ruby​​数组,我们在StackOverflow上找到一

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  4. ruby-on-rails - `a ||= b` 和 `a = b if a.nil 之间的区别? - 2

    我正在检查一个Rails项目。在ERubyHTML模板页面上,我看到了这样几行:我不明白为什么不这样写:在这种情况下,||=和ifnil?有什么区别? 最佳答案 在这种特殊情况下没有区别,但可能是出于习惯。每当我看到nil?被使用时,它几乎总是使用不当。在Ruby中,很少有东西在逻辑上是假的,只有文字false和nil是。这意味着像if(!x.nil?)这样的代码几乎总是更好地表示为if(x)除非期望x可能是文字false。我会将其切换为||=false,因为它具有相同的结果,但这在很大程度上取决于偏好。唯一的缺点是赋值会在每次运行

  5. ruby-on-rails - 如何重命名或移动 Rails 的 README_FOR_APP - 2

    当我在我的Rails应用程序根目录中运行rakedoc:app时,API文档是使用/doc/README_FOR_APP作为主页生成的。我想向该文件添加.rdoc扩展名,以便它在GitHub上正确呈现。更好的是,我想将它移动到应用程序根目录(/README.rdoc)。有没有办法通过修改包含的rake/rdoctask任务在我的Rakefile中执行此操作?是否有某个地方可以查找可以修改的主页文件的名称?还是我必须编写一个新的Rake任务?额外的问题:Rails应用程序的两个单独文件/README和/doc/README_FOR_APP背后的逻辑是什么?为什么不只有一个?

  6. ruby - ruby 中有 each_if 吗? - 2

    假设我在Ruby中有这个each循环。@list.each{|i|putsiifi>10breakend}我想循环遍历列表直到满足条件。这让我感到“不像Ruby”,因为我是Ruby的新手,是否有Ruby方法可以做到这一点? 最佳答案 您可以使用Enumerable#detect或Enumerable#take_while,取决于您想要的结果。@list.detect{|i|putsii>10}#Returnsthefirstelementgreaterthan10,ornil.正如其他人所指出的,更好的风格是先进行子选择,然后再对其

  7. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  8. ruby-on-rails - rbenv:从 RVM 移动到 rbenv 后,在 Jenkins 执行 shell 中找不到命令 - 2

    我从Ubuntu服务器上的RVM转移到rbenv。当我使用RVM时,使用bundle没有问题。转移到rbenv后,我在Jenkins的执行shell中收到“找不到命令”错误。我内爆并删除了RVM,并从~/.bashrc'中删除了所有与RVM相关的行。使用后我仍然收到此错误:rvmimploderm~/.rvm-rfrm~/.rvmrcgeminstallbundlerecho'exportPATH="$HOME/.rbenv/bin:$PATH"'>>~/.bashrcecho'eval"$(rbenvinit-)"'>>~/.bashrc.~/.bashrcrbenvversions

  9. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

  10. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

随机推荐