使用非静态函数重载静态函数的C++方法

44

我想根据函数是静态调用还是通过Foo foo; foo.print();实例化调用来打印两种不同的内容。

编辑:这里是一个类定义,它绝对不能正常工作,已经被一些人回答过了。

class Foo {
    string bla;
    Foo() { bla = "nonstatic"; }

    void print() { cout << bla << endl; }
    static void print() { cout << "static" << endl; }
};

然而,有没有一种好的方法来实现这个效果呢?基本上,我想要做的是:

if(this is a static call)
    do one thing
else
    do another thing

换句话说,我知道PHP可以检查*this变量是否被定义来确定函数是否以静态方式调用。C++是否具有相同的功能?


2
两个版本的 print() 具有相同的签名。我认为它们不能以这种方式进行重载。 - Mahesh
5个回答

66

不行,这是标准直接禁止的:

ISO 14882:2003 C++标准13.1/2 – 可重载声明

某些函数声明不能被重载:

  • 只有返回类型不同的函数声明不能被重载。
  • 如果其中任何一个成员函数声明是 static 成员函数声明(9.4),则具有相同名称和参数类型的成员函数声明不能被重载。

...

[例子:

class X {
    static void f();
    void f();                // ill-formed
    void f() const;          // ill-formed
    void f() const volatile; // ill-formed
    void g();
    void g() const;          // OK: no static g
    void g() const volatile; // OK: no static g
};

—示例结束]

...

此外,即使在实例上调用静态函数也是可能的,因此会存在歧义:

ISO 14882:2003 C++ 标准 9.4/2 – 静态成员

X 的静态成员 s 可以通过限定符表达式 X::s 来引用;不必使用类成员访问语法 (5.2.5) 来引用 static member。可以使用类成员访问语法来引用 static 成员,此时将计算 object-expression[例如:

class process {
public:
        static void reschedule();
}
process& g();
void f()
{
        process::reschedule(); // OK: no object necessary
        g().reschedule();      // g() is called
}

——示例结束]

...

因此,您所拥有的内容存在歧义:

class Foo
{
public:
    string bla;
    Foo() { bla = "nonstatic"; }
    void print() { cout << bla << endl; }
    static void print() { cout << "static" << endl; }
};

int main()
{
    Foo f;
    // Call the static or non-static member function?
    // C++ standard 9.4/2 says that static member
    // functions are callable via this syntax. But
    // since there's also a non-static function named
    // "print()", it is ambiguous.
    f.print();
}
为了回答你关于如何检查成员函数在哪个实例上调用的问题,C++语言中提供了一个关键字 this。它指向调用该函数的对象。然而,this 关键字总是指向一个对象,即使在静态调用情况下也不能为 NULL。因此,不可能像PHP那样检查一个函数是否以静态方式调用。

ISO 14882:2003 C++ Standard 9.3.2/1 – The this pointer

在非静态成员函数(9.3)体内,关键字 this 是一个非左值表达式,其值为调用该函数的对象的地址。


非常感谢您提供非常清晰的答案,但是我表达得不好(对此我深感抱歉),我的问题实际上是想知道静态函数的行为是否可以根据它是从类的实例中调用还是以静态方式调用来确定。请参见我问题的后半部分。 - Alan Turing
@Lex Fridman:我并不认为你的问题问得很差。你问道“C++是否具有相同的能力?”,而我回答说,根据C++标准(定义了这种语言),你不能这样做。 - In silico
我对我的问题表述感到越来越糟糕,尤其是标题。实际上有两个问题。第一个问题是,你能否用非静态方法重载静态方法。这个问题你回答得非常好。第二个问题是,你能否确定在调用静态函数时与类相关联的实例存在。正如你所说,你可以像这样使用静态函数:Foo foo; foo.print(); 我能否从print()的调用中访问foo实例呢? - Alan Turing
@Lex Fridman:啊,好的。你已经为我澄清了,所以不用担心你的措辞。我会回答那部分内容。 - In silico

2

这绝对是不允许的。我看不到任何简洁的方法来实现这一点。你想用这种方式解决什么问题呢?


这有点复杂,但我有一些非静态变量,希望静态函数在从类的实例调用时可以访问它们。我可以给函数取一个不同的名称并使其成为非静态的,但我认为提供一个只有一个函数可供用户使用的一致API会更好。如果这样说不清楚,请见谅。 - Alan Turing
@Lex - 你似乎误解了 'static' 的意思。你无法使用静态方法访问非静态变量;你并没有处理类的实例化。唯一的方法是将类的实例传递给静态方法。 - Brian Roach
我知道 Brian,但是你可以“非静态地”调用静态成员,所以不要使用 Foo::print();,而是使用 Foo foo; foo.print(); - Alan Turing
2
你没有称之为“非静态”...这仍然是在调用类中相同的静态函数,与你所拥有的类实例无关。这与Foo::print();没有任何区别。请参见:https://dev59.com/AHRC5IYBdhLWcg3wW_pk - 我认为这是你困惑的根源。 - Brian Roach
啊,好的,所以在那种情况下foo的实例没有意义,并且无法从print()中访问。感谢您的澄清,Brian。 - Alan Turing

1

答案是否定的,因为你无法基于返回类型进行重载。

你当然可以在一个类中拥有静态方法,但是你不能拥有:

static void foo();
void foo();

因为它们具有相同的方法签名。
编辑:我看到了您的评论,说您想要这样做,并且您想要访问成员变量。您需要执行以下操作:
static void print(Foo f);
void print();
....
static void Foo::print(Foo f)
{
    int a = f.a;
    // do something with a
}

(或在Foo中创建getter和setter等,但这是一般的想法)

但是如果他那样做了,下一个维护者在试图理解为什么有人更喜欢Foo::print(f)而不是f.print()时,会有一个大的"什么鬼"的时刻。 - Bo Persson

1

你不能完全这样做,可以参考In silico's answer

但是你可以让Foo::print()Foo foo; print(foo);做不同的事情。(在与class Foo相同的命名空间中定义void print(Foo& foo),它将被ADL找到)。

无论如何,这不是一个好主意。你有两个非常相似名称的函数,它们做完全不同的事情,这违反了良好的设计原则。


我知道这似乎是糟糕的设计,因为我没有准确地解释我要解决的问题。但实际上,对于我正在做的事情来说,这是有意义的。然而,在C++的上下文中,这可能是一种不好的做法。我知道在像PHP这样的解释性语言中这是一个好的做法。 - Alan Turing
@Lex:这两个函数必须执行不同的任务,因此其中一个函数可以有不同的名称。也许可以使用 Foo::PrintLiveFooCount();Foo foo; foo.print(); - Ben Voigt

0

1) 如果函数的返回类型不同,则无法在方法重载中进行重载。

2) 同样,如果我们在同一类下定义的函数中传递相同的参数,则无法进行重载。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接