可选函数参数:使用默认参数(NULL)还是重载函数?

时间:2022-03-31 11:01:03

I have a function that processes a given vector, but may also create such a vector itself if it is not given.

我有一个处理给定向量的函数,但如果不给出它,也可以创建这样的向量本身。

I see two design choices for such a case, where a function parameter is optional:

在这种情况下,我看到两个设计选择,其中一个函数参数是可选的:

Make it a pointer and make it NULL by default:

设为指针,默认为空:

void foo(int i, std::vector<int>* optional = NULL) {
  if(optional == NULL){
    optional = new std::vector<int>();
    // fill vector with data
  }
  // process vector
}

Or have two functions with an overloaded name, one of which leaves out the argument:

或者有两个具有重载名称的函数,其中一个省略了参数:

void foo(int i) {
   std::vector<int> vec;
   // fill vec with data
   foo(i, vec);
}

void foo(int i, const std::vector<int>& optional) {
  // process vector
}

Are there reasons to prefer one solution over the other?

是否有理由选择一种解决方案而不是另一种?

I slightly prefer the second one because I can make the vector a const reference, since it is, when provided, only read, not written. Also, the interface looks cleaner (isn't NULL just a hack?). And the performance difference resulting from the indirect function call is probably optimized away.

我稍微喜欢第二个,因为我可以使这个向量成为const引用,因为当提供时,它是只读的,而不是写的。此外,这个接口看起来更简洁(NULL不是一种黑客行为吗?)间接函数调用所产生的性能差异可能已经被优化掉了。

Yet, I often see the first solution in code. Are there compelling reasons to prefer it, apart from programmer laziness?

然而,我经常在代码中看到第一个解决方案。除了程序员的懒惰之外,是否有令人信服的理由喜欢它?

12 个解决方案

#1


24  

I would definitely favour the 2nd approach of overloaded methods.

我肯定会支持第二种重载方法。

The first approach (optional parameters) blurs the definition of the method as it no longer has a single well-defined purpose. This in turn increases the complexity of the code, making it more difficult for someone not familiar with it to understand it.

第一种方法(可选参数)模糊了方法的定义,因为它不再具有一个定义明确的目的。这反过来又增加了代码的复杂性,使不熟悉代码的人更难理解它。

With the second approach (overloaded methods), each method has a clear purpose. Each method is well-structured and cohesive. Some additional notes:

使用第二种方法(重载方法),每种方法都有一个明确的目的。每种方法都有良好的结构和内聚性。一些需要注意的地方:

  • If there's code which needs to be duplicated into both methods, this can be extracted out into a separate method and each overloaded method could call this external method.
  • 如果需要将代码复制到这两种方法中,那么可以将其提取到单独的方法中,并且每个重载的方法都可以调用这个外部方法。
  • I would go a step further and name each method differently to indicate the differences between the methods. This will make the code more self-documenting.
  • 我将更进一步,以不同的方式命名每个方法,以表明方法之间的差异。这将使代码更加自我记录。

#2


41  

I would not use either approach.

我不会使用任何一种方法。

In this context, the purpose of foo() seems to be to process a vector. That is, foo()'s job is to process the vector.

在此上下文中,foo()的目的似乎是处理一个向量。也就是说,foo()的工作是处理向量。

But in the second version of foo(), it is implicitly given a second job: to create the vector. The semantics between foo() version 1 and foo() version 2 are not the same.

但是在foo()的第二个版本中,隐式地给出了第二个任务:创建向量。foo()版本1和foo()版本2之间的语义不一样。

Instead of doing this, I would consider having just one foo() function to process a vector, and another function which creates the vector, if you need such a thing.

与此相反,我将考虑只使用一个foo()函数来处理一个向量,如果需要的话,再使用另一个函数来创建这个向量。

For example:

例如:

void foo(int i, const std::vector<int>& optional) {
  // process vector
}

std::vector<int>* makeVector() {
   return new std::vector<int>;
}

Obviously these functions are trivial, and if all makeVector() needs to do to get it's job done is literally just call new, then there may be no point in having the makeVector() function. But I'm sure that in your actual situation these functions do much more than what is being shown here, and my code above illustrates a fundamental approach to semantic design: give one function one job to do.

显然,这些函数都是平凡的,如果所有makeVector()要完成它的工作,只需要调用new,那么拥有makeVector()函数可能就没有意义了。但是我确信,在实际情况中,这些函数的作用要比这里显示的要大得多,我上面的代码演示了语义设计的一个基本方法:给一个函数一个任务。

The design I have above for the foo() function also illustrates another fundamental approach that I personally use in my code when it comes to designing interfaces -- which includes function signatures, classes, etc. That is this: I believe that a good interface is 1) easy and intuitive to use correctly, and 2) difficult or impossible to use incorrectly . In the case of the foo() function we are implictly saying that, with my design, the vector is required to already exist and be 'ready'. By designing foo() to take a reference instead of a pointer, it is both intuitive that the caller must already have a vector, and they are going to have a hard time passing in something that isn't a ready-to-go vector.

上面的设计我已经为foo()函数还演示了另一个基本方法,我个人使用在我的代码设计接口时——包括函数签名,类,等等,是这样的:我相信一个良好的界面是1)简单而直观的使用正确,和2)困难或不可能使用不正确。在foo()函数的例子中,我们含蓄地说,在我的设计中,向量必须已经存在并准备好。通过设计foo()来获取引用而不是指针,这两种方法都是直观的,即调用者必须已经有一个向量,并且他们将很难通过一个不是现成的向量的东西。

#3


18  

While I do understand the complaints of many people regarding default parameters and overloads, there seems to be a lack of understanding to the benefits that these features provide.

虽然我确实理解许多人对默认参数和重载的抱怨,但似乎对这些特性提供的好处缺乏理解。

Default Parameter Values:
First I want to point out that upon initial design of a project, there should be little to no use for defaults if well designed. However, where defaults' greatest assets comes into play is with existing projects and well established APIs. I work on projects that consist of millions of existing lines of code and do not have the luxury to re-code them all. So when you wish to add a new feature which requires an extra parameter; a default is needed for the new parameter. Otherwise you will break everyone that uses your project. Which would be fine with me personally, but I doubt your company or users of your product/API would appreciate having to re-code their projects on every update. Simply, Defaults are great for backwards compatibility! This is usually the reason you will see defaults in big APIs or existing projects.

默认参数值:首先,我想指出,在项目的初始设计阶段,如果设计良好,那么对于默认值应该没有什么用处。然而,违约最重要的资产是存在于现有的项目和建立良好的api中。我从事的项目包含了数百万行现有的代码,并且没有能力对它们全部进行重新编码。所以当你想添加一个新特性时需要一个额外的参数;新参数需要一个默认值。否则你会破坏所有使用你项目的人。这对我个人来说是没问题的,但我怀疑你的公司或你的产品/API的用户是否愿意在每次更新时重新编码他们的项目。简单地说,默认是向后兼容的好方法!这通常是您在大型api或现有项目中看到默认值的原因。

Function Overrides: The benefit of function overrides is that they allow for the sharing of a functionality concept, but with with different options/parameters. However, many times I see function overrides lazily used to provide starkly different functionality, with just slightly different parameters. In this case they should each have separately named functions, pertaining to their specific functionality (As with the OP's example).

函数重写:函数重写的好处是允许共享一个功能概念,但是使用不同的选项/参数。然而,很多时候我看到函数被惰性地用于提供完全不同的功能,只是参数略有不同。在这种情况下,它们每个都应该有单独命名的函数,用于说明它们的特定功能(如OP的示例)。

These, features of c/c++ are good and work well when used properly. Which can be said of most any programming feature. It is when they are abused/misused that they cause problems.

这些,c/c++的特点都很好,使用得当也能很好地工作。这可以说是大多数编程特性。正是当他们被滥用/误用时,才会产生问题。

Disclaimer:
I know that this question is a few years old, but since these answers came up in my search results today (2012), I felt this needed further addressing for future readers.

免责声明:我知道这个问题已经有几年的历史了,但是由于这些答案出现在我今天(2012年)的搜索结果中,我觉得这对于未来的读者来说是需要进一步解决的。

#4


5  

I agree, I would use two functions. Basically, you have two different use cases, so it makes sense to have two different implementations.

我同意,我会用两个函数。基本上,您有两个不同的用例,所以有两个不同的实现是有意义的。

I find that the more C++ code I write, the fewer parameter defaults I have - I wouldn't really shed any tears if the feature was deprecated, though I would have to re-write a shed load of old code!

我发现我编写的c++代码越多,我的参数默认值就越少——如果这个特性被弃用了,我不会流泪,尽管我必须重新编写大量的旧代码!

#5


5  

A references can't be NULL in C++, a really good solution would be to use Nullable template. This would let you do things is ref.isNull()

在c++中引用不能为空,一个很好的解决方案是使用Nullable模板。这会让你做的事情是ref.isNull()

Here you can use this:

这里你可以用这个:

template<class T>
class Nullable {
public:
    Nullable() {
        m_set = false;
    }
    explicit
    Nullable(T value) {
        m_value = value;
        m_set = true;
    }
    Nullable(const Nullable &src) {
        m_set = src.m_set;
        if(m_set)
            m_value = src.m_value;
    }
    Nullable & operator =(const Nullable &RHS) {
        m_set = RHS.m_set;
        if(m_set)
            m_value = RHS.m_value;
        return *this;
    }
    bool operator ==(const Nullable &RHS) const {
        if(!m_set && !RHS.m_set)
            return true;
        if(m_set != RHS.m_set)
            return false;
        return m_value == RHS.m_value;
    }
    bool operator !=(const Nullable &RHS) const {
        return !operator==(RHS);
    }

    bool GetSet() const {
        return m_set;
    }

    const T &GetValue() const {
        return m_value;
    }

    T GetValueDefault(const T &defaultValue) const {
        if(m_set)
            return m_value;
        return defaultValue;
    }
    void SetValue(const T &value) {
        m_value = value;
        m_set = true;
    }
    void Clear()
    {
        m_set = false;
    }

private:
    T m_value;
    bool m_set;
};

Now you can have

现在你可以有

void foo(int i, Nullable<AnyClass> &optional = Nullable<AnyClass>()) {
   //you can do 
   if(optional.isNull()) {

   }
}

#6


3  

I usually avoid the first case. Note that those two functions are different in what they do. One of them fills a vector with some data. The other doesn't (just accept the data from the caller). I tend to name differently functions that actually do different things. In fact, even as you write them, they are two functions:

我通常避免第一种情况。注意,这两个函数的作用是不同的。其中一个用一些数据填充一个向量。另一个不接受(只接受来自调用者的数据)。我倾向于命名不同的函数来做不同的事情。事实上,即使你写它们,它们也是两个函数:

  • foo_default (or just foo)
  • foo_default(或者foo)
  • foo_with_values
  • foo_with_values

At least I find this distinction cleaner in the long therm, and for the occasional library/functions user.

至少,我发现这种区别在长时间内是干净的,对于偶尔的库/函数用户来说也是如此。

#7


2  

I, too, prefer the second one. While there are not much difference between the two, you are basically using the functionality of the primary method in the foo(int i) overload and the primary overload would work perfectly without caring about existence of lack of the other one, so there is more separation of concerns in the overload version.

我也喜欢第二种。虽然两者之间并没有明显的有两个,你基本上是使用功能的主要方法foo(int i)过载和主过载工作完全不关心缺乏另一个的存在,所以有更多的关注点分离的重载版本。

#8


2  

In C++ you should avoid allowing valid NULL parameters whenever possible. The reason is that it substantially reduces callsite documentation. I know this sounds extreme but I work with APIs that take upwards of 10-20 parameters, half of which can validly be NULL. The resulting code is almost unreadable

在c++中,应该尽可能避免允许有效的空参数。原因是它大大减少了callsite文档。我知道这听起来有些极端,但我使用的api有10-20个参数,其中一半可以有效地为NULL。生成的代码几乎不可读

SomeFunction(NULL, pName, NULL, pDestination);

If you were to switch it to force const references the code is simply forced to be more readable.

如果您要转换它,以强制const引用,那么代码将*变得更加可读。

SomeFunction(
  Location::Hidden(),
  pName,
  SomeOtherValue::Empty(),
  pDestination);

#9


2  

I'm squarely in the "overload" camp. Others have added specifics about your actual code example but I wanted to add what I feel are the benefits of using overloads versus defaults for the general case.

我完全处于“超负荷”的阵营。其他人已经添加了关于您的实际代码示例的细节,但是我想添加一些我认为对于一般情况来说,使用重载与默认值的好处。

  • Any parameter can be "defaulted"
  • 任何参数都可以“默认”
  • No gotcha if an overriding function uses a different value for its default.
  • 如果重写函数使用不同的值作为默认值,则没有问题。
  • It's not necessary to add "hacky" constructors to existing types in order to allow them to have default.
  • 不需要向现有类型添加“hacky”构造函数,以便允许它们具有默认值。
  • Output parameters can be defaulted without needing to use pointers or hacky global objects.
  • 输出参数可以默认设置,不需要使用指针或hacky全局对象。

To put some code examples on each:

将一些代码示例分别放在上面:

Any parameter can be defaulted:

任何参数均可默认:

class A {}; class B {}; class C {};

void foo (A const &, B const &, C const &);

inline void foo (A const & a, C const & c)
{
  foo (a, B (), c);    // 'B' defaulted
}

No danger of overriding functions having different values for the default:

在缺省情况下,不存在具有不同值的重写函数的危险:

class A {
public:
  virtual void foo (int i = 0);
};

class B : public A {
public:
  virtual void foo (int i = 100);
};


void bar (A & a)
{
  a.foo ();           // Always uses '0', no matter of dynamic type of 'a'
}

It's not necessary to add "hacky" constructors to existing types in order to allow them to be defaulted:

不需要在现有类型中添加“hacky”构造函数,以允许它们默认:

struct POD {
  int i;
  int j;
};

void foo (POD p);     // Adding default (other than {0, 0})
                      // would require constructor to be added
inline void foo ()
{
  POD p = { 1, 2 };
  foo (p);
}

Output parameters can be defaulted without needing to use pointers or hacky global objects:

输出参数可以不需要使用指针或hacky全局对象就可以发生默认值:

void foo (int i, int & j);  // Default requires global "dummy" 
                            // or 'j' should be pointer.
inline void foo (int i)
{
  int j;
  foo (i, j);
}

The only exception to the rule re overloading versus defaults is for constructors where it's currently not possible for a constructor to forward to another. (I believe C++ 0x will solve that though).

重新重载规则与默认规则的惟一例外是构造函数,在这种情况下,构造函数当前不可能将其转发给另一个构造函数。(我相信c++ 0x可以解决这个问题)。

#10


1  

I would favour a third option: Separate into two functions, but do not overload.

我倾向于第三种选择:分为两个功能,但不要超负荷。

Overloads, by nature, are less usable. They require the user to become aware of two options and figure out what the difference between them is, and if they're so inclined, to also check the documentation or the code to ensure which is which.

超负荷,从本质上来说,是不太有用的。它们要求用户了解两个选项,并找出它们之间的区别,如果它们如此倾斜,也要检查文档或代码以确保哪个是哪个。

I would have one function that takes the parameter, and one that is called "createVectorAndFoo" or something like that (obviously naming becomes easier with real problems).

我将有一个函数来获取参数,一个函数叫做“createVectorAndFoo”或者类似的东西(显然命名对于真正的问题来说更容易)。

While this violates the "two responsibilities for function" rule (and gives it a long name), I believe this is preferable when your function really does do two things (create vector and foo it).

虽然这违反了“函数的两个职责”规则(并给出了一个很长的名字),但我相信当您的函数确实做了两件事(创建vector和foo it)时,这就更好了。

#11


1  

Generally I agree with others' suggestion to use a two-function approach. However, if the vector created when the 1-parameter form is used is always the same, you could simplify things by instead making it static and using a default const& parameter instead:

一般来说,我同意其他人的建议,使用两种功能的方法。但是,如果在使用1参数形式时创建的向量总是相同的,那么您可以将其设置为静态并使用默认的const&parameter来简化:

// Either at global scope, or (better) inside a class
static vector<int> default_vector = populate_default_vector();

void foo(int i, std::vector<int> const& optional = default_vector) {
    ...
}

#12


0  

The first way is poorer because you cannot tell if you accidentally passed in NULL or if it was done on purpose... if it was an accident then you have likely caused a bug.

第一种方法比较穷,因为你无法判断你是否意外地传递了NULL,或者它是故意的。如果是意外,那么你很可能造成了一个bug。

With the second one you can test (assert, whatever) for NULL and handle it appropriately.

对于第二个,您可以测试(assert,无论什么)NULL并适当地处理它。

#1


24  

I would definitely favour the 2nd approach of overloaded methods.

我肯定会支持第二种重载方法。

The first approach (optional parameters) blurs the definition of the method as it no longer has a single well-defined purpose. This in turn increases the complexity of the code, making it more difficult for someone not familiar with it to understand it.

第一种方法(可选参数)模糊了方法的定义,因为它不再具有一个定义明确的目的。这反过来又增加了代码的复杂性,使不熟悉代码的人更难理解它。

With the second approach (overloaded methods), each method has a clear purpose. Each method is well-structured and cohesive. Some additional notes:

使用第二种方法(重载方法),每种方法都有一个明确的目的。每种方法都有良好的结构和内聚性。一些需要注意的地方:

  • If there's code which needs to be duplicated into both methods, this can be extracted out into a separate method and each overloaded method could call this external method.
  • 如果需要将代码复制到这两种方法中,那么可以将其提取到单独的方法中,并且每个重载的方法都可以调用这个外部方法。
  • I would go a step further and name each method differently to indicate the differences between the methods. This will make the code more self-documenting.
  • 我将更进一步,以不同的方式命名每个方法,以表明方法之间的差异。这将使代码更加自我记录。

#2


41  

I would not use either approach.

我不会使用任何一种方法。

In this context, the purpose of foo() seems to be to process a vector. That is, foo()'s job is to process the vector.

在此上下文中,foo()的目的似乎是处理一个向量。也就是说,foo()的工作是处理向量。

But in the second version of foo(), it is implicitly given a second job: to create the vector. The semantics between foo() version 1 and foo() version 2 are not the same.

但是在foo()的第二个版本中,隐式地给出了第二个任务:创建向量。foo()版本1和foo()版本2之间的语义不一样。

Instead of doing this, I would consider having just one foo() function to process a vector, and another function which creates the vector, if you need such a thing.

与此相反,我将考虑只使用一个foo()函数来处理一个向量,如果需要的话,再使用另一个函数来创建这个向量。

For example:

例如:

void foo(int i, const std::vector<int>& optional) {
  // process vector
}

std::vector<int>* makeVector() {
   return new std::vector<int>;
}

Obviously these functions are trivial, and if all makeVector() needs to do to get it's job done is literally just call new, then there may be no point in having the makeVector() function. But I'm sure that in your actual situation these functions do much more than what is being shown here, and my code above illustrates a fundamental approach to semantic design: give one function one job to do.

显然,这些函数都是平凡的,如果所有makeVector()要完成它的工作,只需要调用new,那么拥有makeVector()函数可能就没有意义了。但是我确信,在实际情况中,这些函数的作用要比这里显示的要大得多,我上面的代码演示了语义设计的一个基本方法:给一个函数一个任务。

The design I have above for the foo() function also illustrates another fundamental approach that I personally use in my code when it comes to designing interfaces -- which includes function signatures, classes, etc. That is this: I believe that a good interface is 1) easy and intuitive to use correctly, and 2) difficult or impossible to use incorrectly . In the case of the foo() function we are implictly saying that, with my design, the vector is required to already exist and be 'ready'. By designing foo() to take a reference instead of a pointer, it is both intuitive that the caller must already have a vector, and they are going to have a hard time passing in something that isn't a ready-to-go vector.

上面的设计我已经为foo()函数还演示了另一个基本方法,我个人使用在我的代码设计接口时——包括函数签名,类,等等,是这样的:我相信一个良好的界面是1)简单而直观的使用正确,和2)困难或不可能使用不正确。在foo()函数的例子中,我们含蓄地说,在我的设计中,向量必须已经存在并准备好。通过设计foo()来获取引用而不是指针,这两种方法都是直观的,即调用者必须已经有一个向量,并且他们将很难通过一个不是现成的向量的东西。

#3


18  

While I do understand the complaints of many people regarding default parameters and overloads, there seems to be a lack of understanding to the benefits that these features provide.

虽然我确实理解许多人对默认参数和重载的抱怨,但似乎对这些特性提供的好处缺乏理解。

Default Parameter Values:
First I want to point out that upon initial design of a project, there should be little to no use for defaults if well designed. However, where defaults' greatest assets comes into play is with existing projects and well established APIs. I work on projects that consist of millions of existing lines of code and do not have the luxury to re-code them all. So when you wish to add a new feature which requires an extra parameter; a default is needed for the new parameter. Otherwise you will break everyone that uses your project. Which would be fine with me personally, but I doubt your company or users of your product/API would appreciate having to re-code their projects on every update. Simply, Defaults are great for backwards compatibility! This is usually the reason you will see defaults in big APIs or existing projects.

默认参数值:首先,我想指出,在项目的初始设计阶段,如果设计良好,那么对于默认值应该没有什么用处。然而,违约最重要的资产是存在于现有的项目和建立良好的api中。我从事的项目包含了数百万行现有的代码,并且没有能力对它们全部进行重新编码。所以当你想添加一个新特性时需要一个额外的参数;新参数需要一个默认值。否则你会破坏所有使用你项目的人。这对我个人来说是没问题的,但我怀疑你的公司或你的产品/API的用户是否愿意在每次更新时重新编码他们的项目。简单地说,默认是向后兼容的好方法!这通常是您在大型api或现有项目中看到默认值的原因。

Function Overrides: The benefit of function overrides is that they allow for the sharing of a functionality concept, but with with different options/parameters. However, many times I see function overrides lazily used to provide starkly different functionality, with just slightly different parameters. In this case they should each have separately named functions, pertaining to their specific functionality (As with the OP's example).

函数重写:函数重写的好处是允许共享一个功能概念,但是使用不同的选项/参数。然而,很多时候我看到函数被惰性地用于提供完全不同的功能,只是参数略有不同。在这种情况下,它们每个都应该有单独命名的函数,用于说明它们的特定功能(如OP的示例)。

These, features of c/c++ are good and work well when used properly. Which can be said of most any programming feature. It is when they are abused/misused that they cause problems.

这些,c/c++的特点都很好,使用得当也能很好地工作。这可以说是大多数编程特性。正是当他们被滥用/误用时,才会产生问题。

Disclaimer:
I know that this question is a few years old, but since these answers came up in my search results today (2012), I felt this needed further addressing for future readers.

免责声明:我知道这个问题已经有几年的历史了,但是由于这些答案出现在我今天(2012年)的搜索结果中,我觉得这对于未来的读者来说是需要进一步解决的。

#4


5  

I agree, I would use two functions. Basically, you have two different use cases, so it makes sense to have two different implementations.

我同意,我会用两个函数。基本上,您有两个不同的用例,所以有两个不同的实现是有意义的。

I find that the more C++ code I write, the fewer parameter defaults I have - I wouldn't really shed any tears if the feature was deprecated, though I would have to re-write a shed load of old code!

我发现我编写的c++代码越多,我的参数默认值就越少——如果这个特性被弃用了,我不会流泪,尽管我必须重新编写大量的旧代码!

#5


5  

A references can't be NULL in C++, a really good solution would be to use Nullable template. This would let you do things is ref.isNull()

在c++中引用不能为空,一个很好的解决方案是使用Nullable模板。这会让你做的事情是ref.isNull()

Here you can use this:

这里你可以用这个:

template<class T>
class Nullable {
public:
    Nullable() {
        m_set = false;
    }
    explicit
    Nullable(T value) {
        m_value = value;
        m_set = true;
    }
    Nullable(const Nullable &src) {
        m_set = src.m_set;
        if(m_set)
            m_value = src.m_value;
    }
    Nullable & operator =(const Nullable &RHS) {
        m_set = RHS.m_set;
        if(m_set)
            m_value = RHS.m_value;
        return *this;
    }
    bool operator ==(const Nullable &RHS) const {
        if(!m_set && !RHS.m_set)
            return true;
        if(m_set != RHS.m_set)
            return false;
        return m_value == RHS.m_value;
    }
    bool operator !=(const Nullable &RHS) const {
        return !operator==(RHS);
    }

    bool GetSet() const {
        return m_set;
    }

    const T &GetValue() const {
        return m_value;
    }

    T GetValueDefault(const T &defaultValue) const {
        if(m_set)
            return m_value;
        return defaultValue;
    }
    void SetValue(const T &value) {
        m_value = value;
        m_set = true;
    }
    void Clear()
    {
        m_set = false;
    }

private:
    T m_value;
    bool m_set;
};

Now you can have

现在你可以有

void foo(int i, Nullable<AnyClass> &optional = Nullable<AnyClass>()) {
   //you can do 
   if(optional.isNull()) {

   }
}

#6


3  

I usually avoid the first case. Note that those two functions are different in what they do. One of them fills a vector with some data. The other doesn't (just accept the data from the caller). I tend to name differently functions that actually do different things. In fact, even as you write them, they are two functions:

我通常避免第一种情况。注意,这两个函数的作用是不同的。其中一个用一些数据填充一个向量。另一个不接受(只接受来自调用者的数据)。我倾向于命名不同的函数来做不同的事情。事实上,即使你写它们,它们也是两个函数:

  • foo_default (or just foo)
  • foo_default(或者foo)
  • foo_with_values
  • foo_with_values

At least I find this distinction cleaner in the long therm, and for the occasional library/functions user.

至少,我发现这种区别在长时间内是干净的,对于偶尔的库/函数用户来说也是如此。

#7


2  

I, too, prefer the second one. While there are not much difference between the two, you are basically using the functionality of the primary method in the foo(int i) overload and the primary overload would work perfectly without caring about existence of lack of the other one, so there is more separation of concerns in the overload version.

我也喜欢第二种。虽然两者之间并没有明显的有两个,你基本上是使用功能的主要方法foo(int i)过载和主过载工作完全不关心缺乏另一个的存在,所以有更多的关注点分离的重载版本。

#8


2  

In C++ you should avoid allowing valid NULL parameters whenever possible. The reason is that it substantially reduces callsite documentation. I know this sounds extreme but I work with APIs that take upwards of 10-20 parameters, half of which can validly be NULL. The resulting code is almost unreadable

在c++中,应该尽可能避免允许有效的空参数。原因是它大大减少了callsite文档。我知道这听起来有些极端,但我使用的api有10-20个参数,其中一半可以有效地为NULL。生成的代码几乎不可读

SomeFunction(NULL, pName, NULL, pDestination);

If you were to switch it to force const references the code is simply forced to be more readable.

如果您要转换它,以强制const引用,那么代码将*变得更加可读。

SomeFunction(
  Location::Hidden(),
  pName,
  SomeOtherValue::Empty(),
  pDestination);

#9


2  

I'm squarely in the "overload" camp. Others have added specifics about your actual code example but I wanted to add what I feel are the benefits of using overloads versus defaults for the general case.

我完全处于“超负荷”的阵营。其他人已经添加了关于您的实际代码示例的细节,但是我想添加一些我认为对于一般情况来说,使用重载与默认值的好处。

  • Any parameter can be "defaulted"
  • 任何参数都可以“默认”
  • No gotcha if an overriding function uses a different value for its default.
  • 如果重写函数使用不同的值作为默认值,则没有问题。
  • It's not necessary to add "hacky" constructors to existing types in order to allow them to have default.
  • 不需要向现有类型添加“hacky”构造函数,以便允许它们具有默认值。
  • Output parameters can be defaulted without needing to use pointers or hacky global objects.
  • 输出参数可以默认设置,不需要使用指针或hacky全局对象。

To put some code examples on each:

将一些代码示例分别放在上面:

Any parameter can be defaulted:

任何参数均可默认:

class A {}; class B {}; class C {};

void foo (A const &, B const &, C const &);

inline void foo (A const & a, C const & c)
{
  foo (a, B (), c);    // 'B' defaulted
}

No danger of overriding functions having different values for the default:

在缺省情况下,不存在具有不同值的重写函数的危险:

class A {
public:
  virtual void foo (int i = 0);
};

class B : public A {
public:
  virtual void foo (int i = 100);
};


void bar (A & a)
{
  a.foo ();           // Always uses '0', no matter of dynamic type of 'a'
}

It's not necessary to add "hacky" constructors to existing types in order to allow them to be defaulted:

不需要在现有类型中添加“hacky”构造函数,以允许它们默认:

struct POD {
  int i;
  int j;
};

void foo (POD p);     // Adding default (other than {0, 0})
                      // would require constructor to be added
inline void foo ()
{
  POD p = { 1, 2 };
  foo (p);
}

Output parameters can be defaulted without needing to use pointers or hacky global objects:

输出参数可以不需要使用指针或hacky全局对象就可以发生默认值:

void foo (int i, int & j);  // Default requires global "dummy" 
                            // or 'j' should be pointer.
inline void foo (int i)
{
  int j;
  foo (i, j);
}

The only exception to the rule re overloading versus defaults is for constructors where it's currently not possible for a constructor to forward to another. (I believe C++ 0x will solve that though).

重新重载规则与默认规则的惟一例外是构造函数,在这种情况下,构造函数当前不可能将其转发给另一个构造函数。(我相信c++ 0x可以解决这个问题)。

#10


1  

I would favour a third option: Separate into two functions, but do not overload.

我倾向于第三种选择:分为两个功能,但不要超负荷。

Overloads, by nature, are less usable. They require the user to become aware of two options and figure out what the difference between them is, and if they're so inclined, to also check the documentation or the code to ensure which is which.

超负荷,从本质上来说,是不太有用的。它们要求用户了解两个选项,并找出它们之间的区别,如果它们如此倾斜,也要检查文档或代码以确保哪个是哪个。

I would have one function that takes the parameter, and one that is called "createVectorAndFoo" or something like that (obviously naming becomes easier with real problems).

我将有一个函数来获取参数,一个函数叫做“createVectorAndFoo”或者类似的东西(显然命名对于真正的问题来说更容易)。

While this violates the "two responsibilities for function" rule (and gives it a long name), I believe this is preferable when your function really does do two things (create vector and foo it).

虽然这违反了“函数的两个职责”规则(并给出了一个很长的名字),但我相信当您的函数确实做了两件事(创建vector和foo it)时,这就更好了。

#11


1  

Generally I agree with others' suggestion to use a two-function approach. However, if the vector created when the 1-parameter form is used is always the same, you could simplify things by instead making it static and using a default const& parameter instead:

一般来说,我同意其他人的建议,使用两种功能的方法。但是,如果在使用1参数形式时创建的向量总是相同的,那么您可以将其设置为静态并使用默认的const&parameter来简化:

// Either at global scope, or (better) inside a class
static vector<int> default_vector = populate_default_vector();

void foo(int i, std::vector<int> const& optional = default_vector) {
    ...
}

#12


0  

The first way is poorer because you cannot tell if you accidentally passed in NULL or if it was done on purpose... if it was an accident then you have likely caused a bug.

第一种方法比较穷,因为你无法判断你是否意外地传递了NULL,或者它是故意的。如果是意外,那么你很可能造成了一个bug。

With the second one you can test (assert, whatever) for NULL and handle it appropriately.

对于第二个,您可以测试(assert,无论什么)NULL并适当地处理它。