【问题标题】:What's the safest way to define short function name aliases in C++?在 C++ 中定义短函数名别名的最安全方法是什么?
【发布时间】:2012-12-15 01:26:16
【问题描述】:

假设我在文件utility.h 中有一个类Utility

class Utility {
public:
    static double longDescriptiveName(double x) { return x + 42; }
};

然后我发现我经常使用函数longDescriptiveName(...)。因此,当我喝了太多咖啡时,我就像一个不负责任的 C++ 程序员一样,创建了一个新文件 utilitymacros.h 并在其中添加以下内容:

#define ldn Utility::longDescriptiveName

现在我将"utilitymacros.h" 包含在我使用ldn(...) 的任何*.cpp 中,我的内心充满喜悦,因为输入3 个字母比28 个字母更方便。

问题:有没有比#define 更安全(更合适)的方法?

我注意到在包含 boost 标头之后我必须包含“utilitymacros.h”,我显然不喜欢它,因为它是冲突的标志(尽管我得到的 Boost 错误并不清楚冲突是)。

说明 1:关于代码可读性

如果您可能会说这会对代码的可读性产生负面影响,我向您保证不会,因为它是一小组经常使用的函数。一个广为人知的例子是stoi 代表stringToInteger。另一个是pdf for probabilityDensityFunction 等。所以如果我想做以下事情,stoi 在我看来更具可读性:

int x = stoi(a) + stoi(b) + stoi(c) + stoi(d);

比:

int x = Utility::stringToInteger(a) + Utility::stringToInteger(b)
        + Utility::stringToInteger(c) + Utility::stringToInteger(d);

或者:

int x = Utility::stringToInteger(a);
x += Utility::stringToInteger(b);
x += Utility::stringToInteger(c);
x += Utility::stringToInteger(d);

说明 2:编辑器宏

我使用 Emacs 作为我选择的 IDE 和 Kinesis 键盘,所以您知道我使用了大量的键盘宏、自定义键盘快捷键,以及实际修改我在编辑器中看到的内容与实际存储在 h/cpp 中的内容文件。但是,我仍然觉得在一些特定情况下使用函数缩写的简单性和视觉可读性(如上所述)确实是我正在寻找的结果(这当然受一定程度的影响)。

【问题讨论】:

  • 我真的不认为这是一个好主意,因为它会损害代码的可读性。
  • 我认为pdf 是一种文件格式,并且真的感到困惑。角色是免费的,不使用它们并不能拯救环境。
  • 好吧,也许短名称对您来说是可读的,但对其他人来说很难,即使(或特别是)它们在任何地方都使用。在我的旧版本中,有很多 3 个字符的函数名称,这也是我退出那里的众多原因之一:代码混乱不堪。当然,不仅仅是因为函数和变量名,而是因为它是其中的一部分。

标签: c++ function macros alias c-preprocessor


【解决方案1】:

在你的文本编辑器中配置一个片段/宏/类似的东西怎么样?这样你只需要输入 ldn 或类似的东西,代码就不必通过预处理器运行,以后很难找到错误。

【讨论】:

  • 另外,打出来就明白了。
【解决方案2】:

您可以编写inline 函数来代替宏,将调用转发到实际函数:

inline double ldn(double x)
{
   return Utility::longDescriptiveName(x);
}

这肯定比宏更安全

【讨论】:

  • +1 内联函数肯定更安全,因为有类型检查,我认为建议使用(C++ 风格)而不是#define(C 风格和不太安全)。旁注:与宏不同,内联缺乏使用通过使用内联模板来完成的任何数据类型!
  • @Nawaz,我假设内联函数不是 Utility 类的一部分,对吗?在这种情况下,您是否建议将内联定义放在与utility.h 相同的标头中,或者为它们创建一个单独的标头,例如utilityhelpers.h
  • @LexFridman:是的。它在课堂之外Utility。您可以将它放在同一个标​​题中,但在类之外。这取决于你。
【解决方案3】:

您可以使用函数引用:

double (&ldn)(double) = Utility::longDescriptiveName;

【讨论】:

  • 不应该是static double (&ldn)(double) = ...吗?
  • 在 C++11 中,auto &&ldn = Utility::longDescriptiveName; 非常地道。无论如何static的优势是什么?
  • @Potatoswatter:在头文件中; static 用于避免多定义错误。
  • @Nawaz static 用于定义多个不同的同名对象。使用extern 可能会更好。无论如何,通常首选未命名的命名空间。
  • 使用函数引用可能会影响函数调用的内联能力。
【解决方案4】:

我不知道这是否有帮助,但我认为部分问题可能是使用了过于笼统的命名空间(或类名,在这种情况下),例如 Utility

如果不是Utility::stringToInteger,我们有

namespace utility {
  namespace type_conversion {
    namespace string {
      int to_int(const std::string &s);
    }
  }
}

那么这个函数就可以像这样在本地使用:

void local_function()
{
  using namespace utility::type_conversion::string;

  int sum = to_int(a) + to_int(b) + to_int(c) + to_int(d);
}

类似地,如果使用类/结构和静态函数(这可能有充分的理由),我们有类似的东西

strut utility {
  struct type_conversion {
    struct string {
      static int to_int(const std::string &s);
    };
  };
};

本地函数看起来像这样:

void local_function()
{
  typedef utility::type_conversion::string str;

  int sum = str::to_int(a) + str::to_int(b)
              + str::to_int(c) + str::to_int(d);
}

我意识到我并没有告诉你任何你不知道的语法;它更多地提醒人们,命名空间和类的组织和结构本身在使代码更具可读性(和可写性)方面发挥着重要作用。

【讨论】:

    【解决方案5】:

    另一种方法是重命名您的函数并将其放在命名空间而不是类中,因为它无论如何都是静态的。 utility.h变成了

    namespace Utility {
        // long descriptive comment
        inline double ldn(double x) { return x + 42; }
    }
    

    然后您可以将using namespace Utility; 放入您的客户端代码中。

    我知道有很多风格指南说短名称是一件坏事,但我不明白遵循某种风格然后规避它的意义。

    【讨论】:

      【解决方案6】:

      您可以使用别名模板(C++11 起)。

        using shortName = my::complicate::function::name;
      

      【讨论】:

        猜你喜欢
        • 2012-11-21
        • 2012-01-02
        • 2011-11-12
        • 1970-01-01
        • 2018-05-12
        • 1970-01-01
        • 2013-06-18
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多