重载函数以获取指针或引用是不好的做法吗?

Is it bad practice to overload a function to take both a pointer or a reference?

例如,如果我有一个结构 FooSpec:

struct FooSpec { /* lots of data */ };

提供以下 both 作为重载函数是不好的做法吗?还是我应该只选择一个并坚持下去?

Foo *createFoo(const FooSpec *spec) { /* ... */ }
Foo *createFoo(const FooSpec &spec) { return createFoo(&spec); }

两者都会导致意想不到的问题吗?

做一件特定的事情应该只有一种方法。这将使代码对用户来说更清晰。因此,您应该只选择一个,具体取决于语义:

  • 如果 nullptr 是要传递给 createFoo 的有意义的值,则声明该函数采用指针。

  • 如果不应将 nullptr 传递给 createFoo,则将参数声明为引用,以使编译器对尝试传递 [=10] 的人大喊大叫=].

现在您的代码的用户将 "Ah, I can pass a null to that function, cool!" 或 "Okay this function needs an actual object to work properly"。

在整个项目中坚持这个约定,使代码的界面更加连贯。

或者如WhozCraig所说,

Pick the appropriate model and tell the clients "this is your interface."

软件代码库不是静态对象,它会随着用户的需求而发展。如果一个令人困惑的界面是"bad practice"(我宁愿说一件愚蠢的事情),代码库可能最终会出现这样的情况,因为它是从重构和代码改进中发展而来的。

在这种情况下,如果您正在迁移编码约定以使用引用而不是 const 指针(或相反,但这对我来说意义不大),这并不是坏习惯。这里的错误做法可能是进行这些演变而不记录它,而其他人不知道这些方法中的一种已被弃用,或者不知道使用哪一种。

还有可能使用 const 指针传递 nullptr_t(或像我这样的老计时器的 NULL 指针)。我不确定这样做的好处,因为您可以在它们的参数上重载方法,但同样在不断变化的 API 的上下文中它是有意义的。


† 还活着!!!

我能想到一个有点用的理由来做这件事。

Foo* 版本可以检查是否为 null,处理该情况,如果没有则使用参考案例。

然后 Foo& 不能检查空值。

这让调用者说 "I know this is not null because I checked",在函数内保存一个分支。

然而,一旦该方法的成本超过了一个非常低的阈值,这种好处(保存分支)就毫无意义了。