【问题标题】:Avoiding need to specify using operator=避免需要使用 operator= 指定
【发布时间】:2021-05-13 22:36:07
【问题描述】:

我正在尝试创建一个基类,用于围绕各种作用域操作创建帮助器,为此,基类允许分配可调用对象。一个简单的谓词确定是否调用了可调用对象。无论如何,总是调用 dtor。

问题是每个派生类都需要显式继承 operator=。有没有办法编写基类,这样就不需要了?

#include <functional>
#include <iostream>

using callback_t = std::function<void(void)>;

struct C
{
    constexpr C(bool ok) : ok_{ok} {}
    bool ok_ {false};

    bool operator=(callback_t fn) const noexcept {
        return ok_ ? fn(), true : false;
    }

    ~C() noexcept {
        std::cout << "end\n";
    }
};

struct D final : public C
{
    using C::operator=;
};

int main()
{
    auto predicate = [] { return true; };

    D{predicate()} = [] {
        std::cout << "ok\n";
    };
    D{false} = [] {
        std::cout << "wrong\n";
    };
}

(也是https://gcc.godbolt.org/z/eh8ncffos

struct D中删除行

    using C::operator=;

导致编译错误,因为Ds 默认的复制操作符抢占了 C 的自定义操作符=。

<source>: In function 'int main()':
<source>:24:9: error: no match for 'operator=' (operand types are 'D' and 'main()::<lambda()>')
24 |         };
    |         ^
<source>:14:12: note: candidate: 'constexpr D& D::operator=(const D&)'
14 |     struct D final : public C
    |            ^
<source>:14:12: note:   no known conversion for argument 1 from 'main()::<lambda()>' to 'const D&'
<source>:14:12: note: candidate: 'constexpr D& D::operator=(D&&)'
<source>:14:12: note:   no known conversion for argument 1 from 'main()::<lambda()>' to 'D&&'
<source>:27:9: error: no match for 'operator=' (operand types are 'D' and 'main()::<lambda()>')
27 |         };
    |         ^
<source>:14:12: note: candidate: 'constexpr D& D::operator=(const D&)'
14 |     struct D final : public C
    |            ^
<source>:14:12: note:   no known conversion for argument 1 from 'main()::<lambda()>' to 'const D&'
<source>:14:12: note: candidate: 'constexpr D& D::operator=(D&&)'
<source>:14:12: note:   no known conversion for argument 1 from 'main()::<lambda()>' to 'D&&'

【问题讨论】:

  • 尝试使用 CRTP 吗?
  • @IgorR。如果它只是默认的 operator=,我会这样做,但这是一个转换运算符,我似乎无法通过 crtp 实现这一点:gcc.godbolt.org/z/neKGzK8oa
  • 一定要拼写operator=吗?而不是喜欢... call?
  • @Barry Blame boost/ut; tldr 我们不想在传递给它的 lambda 周围添加括号。它被用作一种通用范围包装器/延迟机制:gcc.godbolt.org/z/E8hhrxhd3
  • @Barry(考虑到条件执行,我更喜欢 'operator&&',但我仍然想知道它可以通过 operator= 来实现,而不需要 'using' 术语)

标签: c++ c++17 c++20


【解决方案1】:

问题是每个派生类都需要显式继承operator=。有没有办法编写基类,这样就不需要了?

不,这是不可能的。

规则是不同作用域的函数不会重载。因为派生类总是有一个名为operator=的东西(派生类自己声明一个,或者复制赋值运算符被隐式声明......即使它被定义为删除)。因此,当您执行derived = x; 时,operator= 的名称查找将从Derived 开始,并且在找到候选人后,就停在那里。它不会继续研究基类......除非你用 using-declaration 引入那个候选人,就像你展示的问题一样。

仍然使用operator= 但将功能隐藏在某处的唯一方法是实际反转您的类层次结构。而不是:

struct C {
    // ... implementation here ...
};

struct D : C {
    using C::operator=;
};

// use D

你可以这样做:

template <typename Derived>
struct C {
    // implementation here
};

struct D { }

// use C<D>

这样,C 实际上就是你正在与之交互的类,所以它的operator= 将是第一个考虑的候选对象。


或者,如果您只是不使用= 作为此操作的拼写,则所有其他运算符都可用,并且它们不需要派生类中的 using-declaration 即可工作。

鉴于the comment 希望避免使用括号,您不需要括号的二元运算符列表是:-&gt;*+-*/%、@ 987654339@、&amp;|&amp;&amp;||&lt;&gt;&lt;&lt;&gt;&gt;(以及这些的复合赋值版本)。

我故意将==&lt;=&gt; 从列表中排除,因为它们很特别,只是没有。

【讨论】:

    猜你喜欢
    • 2017-07-09
    • 1970-01-01
    • 2017-12-04
    • 1970-01-01
    • 2018-07-30
    • 1970-01-01
    • 1970-01-01
    • 2023-01-25
    • 2018-08-20
    相关资源
    最近更新 更多