28 December 2022

C++ 核心指南目录

“Create an overload set for a derived class and its bases with using”

理由

不使用 using 的话,派生类的成员函数会覆盖从基类继承的所有重载函数。

坏例子

#include <iostream>
class B {
public:
    virtual int f(int i) { std::cout << "f(int): "; return i; }
    virtual double f(double d) { std::cout << "f(double): "; return d; }
    virtual ~B() = default;
};
class D: public B {
public:
    int f(int i) override { std::cout << "f(int): "; return i + 1; }
};
int main()
{
    D d;
    std::cout << d.f(2) << '\n';   // prints "f(int): 3"
    std::cout << d.f(2.3) << '\n'; // prints "f(int): 3"
}
f(int): 3
f(int): 3

好例子

#include <iostream>
class B {
public:
    virtual int f(int i) { std::cout << "f(int): "; return i; }
    virtual double f(double d) { std::cout << "f(double): "; return d; }
    virtual ~B() = default;
};
class D: public B {
public:
    int f(int i) override { std::cout << "f(int): "; return i + 1; }
    using B::f; // exposes f(double)
};
int main()
{
    D d;
    std::cout << d.f(2) << '\n';   // prints "f(int): 3"
    std::cout << d.f(2.3) << '\n'; // prints "f(double): 3"
}
f(int): 3
f(double): 2.3

注意

这个问题会影响到虚的以及非虚的成员函数。

对于变参数模板类, C++17 提供了一个带可变参数形式的 using 声明:

#include <iostream>
class B {
public:
    virtual void operator()(int x) {
        std::cout << "B( " << x << " )" << std::endl;
    }
    virtual ~B() = default;
};

template<class... Ts>
struct Overloader : Ts... {
    using Ts::operator()...; // exposes operator() from every base
    void operator()() { std::cout << "Overloader()\n"; }
};

int main()
{
    Overloader<B> d;
    d(42);
    d();
}
B( 42 )
Overloader()

强化

诊断名字隐藏的情况