可空引用的C ++惯用方式

时间:2019-07-06 20:32:03

标签: c++ reference nullable

我正在编写一个函数,该函数应接受const std::string &,但也应接受一个特殊的nullptr值。目前,我正在使用const std::string *。但是,我觉得应该有更多的C ++方式来执行此任务。

我最强的对点是按值传递(字符串不会很长,但是我不想复制64kB)。这也意味着我不希望像optional<T>这样的对象具有T类型的值字段。另外,我不想使用任何外部库。我的项目没有使用Boost或GSL,而只是使用纯C ++(确切地说是C ++ 17)。

是否存在标准的库类(奇迹发生在namespace std中)或这种情况的广泛接受的习惯用法?

5 个答案:

答案 0 :(得分:6)

如果std::optional支持引用,那将是一种很好的“现代”方式。

但是:

  • 没有,
  • 有时看中和“现代”并不是它被破解的全部。

古老,久经考验且清晰易读的解决方案是使用指针。这种方法绝对可以完成您需要做的所有事情。

只需这样做,然后继续将您的宝贵时间花在更重要的事情上!

答案 1 :(得分:3)

这个问题的一个有趣方面是,我更普遍地看到了相反的观点:指定不允许为空的指针的C ++方法是什么?答案归结为相同的原则。

指针与引用之间的主要区别是:

  1. 可以使一个指针指向另一个对象。
  2. 指针可以为空。

如果使指针恒定,则第一个差异消失。 C ++具有“空引用”的方法是具有常量指针。请注意,我所说的是指针是常量,如T * const,它独立于指向对象的常量是常量,如T const *const T *

您可能已经被训练为认为所有指针都是邪恶的,但这过于笼统了。原始指针是引用可​​选对象的一种好方法,这些对象是您不负责负责的。如果您负责释放内存,请使用智能指针,但如果其他人为您持有该内存,请使用引用或指针。如果需要该对象,请使用引用(不能为null);否则为null。一个指针,如果它是可选的(可以为null)。哦,这是假设您不需要更改所指向的对象,在这种情况下,不再需要引用。

话虽如此,在特定情况下可能会有其他选择。如果在空值和非空值之间没有共享代码,则最好使用重载函数,例如void foo(const std::string &)void foo()。必须权衡各种折衷,包括API一致性。选择最适合您的情况的

答案 2 :(得分:1)

另一种方法是提供一个空字符串,您的函数可以通过对象身份检查来检测该空字符串:

#include <string>

namespace MyLibrary
{
   static const std::string NULL_STRING;

   void foo(const std::string& str)
   {
      if (&str == &NULL_STRING)
         // ...
      else
         // ...
   }
}

int main()
{
   MyLibrary::foo("Hello, world!");
   MyLibrary::foo("");

   MyLibrary::foo(MyLibrary::NULL_STRING);
}

但是,有人可能会认为这有点浪费空间,我认为它不是特别习惯。但是,有时候,我在私有库代码中也采用了类似的方法(特别是与默认参数结合使用)。

答案 3 :(得分:1)

正如您特别要求的一些STD库奇迹:STD库实际上具有reference_wrapper,可以与optional结合使用,以某种方式实现您的要求:

template <class T>
using optional_ref = std::optional<std::reference_wrapper<T>>;

实际上很简单,但是访问起来也很麻烦:

void foo(optional_ref<std::string> str)
{
    if (str)
        printf("%s", str->get().c_str());
}

在呼叫站点上,它的工作效果非常好:

std::string str;
foo(str); // passes a reference
foo({}); // passes nothing

话虽如此,我也不认为使用;)(请参见已接受的答案)是个好主意。

答案 4 :(得分:-1)

编写两个函数:

void f(const string *s)
{
    if (s)
    {
        // Use s
    }
    else
    {
        // Don't use s
    }
}

void f(const string &s)
{
    f(&s);
}