【问题标题】:Can I differentiate between one and multiple arguments?我可以区分一个参数和多个参数吗?
【发布时间】:2013-10-08 16:50:30
【问题描述】:

C++11 或 C++14 中是否有任何新工具可以让我们停止使用:

TRACE0("one-string-no-args");  

TRACE("formatting string with one-or-more args", arg1, arg2, etc);  

这些函数必须通过名称来区分,因为据我所知,重载无法区分零附加参数情况和一个或多个参数情况。

这对于printf 样式接口当然很重要,能够知道不再有参数意味着您可以通过不尝试将格式化字符串解释为格式化字符串来避免一类错误,但仅作为最终输出字符串。

【问题讨论】:

  • void trace(string); vs template<class... Ts> void trace(string, Ts...) 对于编译器来说是完全可以区分的。
  • 是的,可变参数模板就是这样做的。如果你不知道这个名字,这里就是。 “安全”打印函数是可变参数模板的典型介绍性示例。
  • 顺便说一句,我从来没有使用过它,但是如果你真的想把它提升到一个新的水平,boost 有一个很酷的named function parameter module
  • TRACE 看起来像一个宏
  • “这些函数必须通过名称来区分,因为据我所知,重载无法区分零附加参数情况和一个或多个参数情况。”据我所知,C++98 中的重载可以毫无问题地区分具有一个参数的函数和具有两个或更多参数的函数:void foo(int); template <typename T> void foo(int, T, ...);(当然,只有在您想要第二个参数尽可能使用可变参数来改变。)

标签: c++ c++11 printf variadic-functions


【解决方案1】:

您可能只是不知道该功能的名称:variadic templates

它的主要用途是从可变数量的参数中推导出可变数量的类型;您可以将其全部存储在某个地方(例如 std::make_tuple()),或者直接打开参数来使用它们。

这是一个基本用法:

void print() {}

template<class Head, class... Tail>
void print(Head h, Tail... t)
{
    cout << h << endl;
    print(t...);
}

int main()
{
    print(3, "hello", 4.5);
}

在此处查看实际操作:http://ideone.com/VA7YGK

如您所见,它看起来像函数式编程(因为它是!),在其中您匹配将参数列表拆分为头部和尾部的规则,然后用少一个元素调用自身。

另一个例子,来自最近的一个问题,我在其中递归地定义了一个数据结构(简短且简单易读):Multikey map using variadic templates

std::thread 构造函数是另一个例子,一旦它开始在生成的线程中运行,它需要可变数量的参数来将它们提供给函数;几乎 C++11 中与函数交互的所有新内容(例如 std::function)都使用可变参数模板,因此它们可以接受任意数量的任何类型的参数。 std::tuplestd::make_tuple()std::tie() 也可以使用它。

在网上搜索你会发现很多更高级的用法。特别注意参数扩展和完美转发的规则。

【讨论】:

    【解决方案2】:

    对于TRACE 宏(通常包含在 Microsoft 的调试宏中),不,该语言将没有处理预处理器宏的“新”方法(实际上与以前相同)。

    对于一般函数,始终支持可变参数函数(例如printf)。

    【讨论】:

      【解决方案3】:

      这对某些人来说可能看起来很愚蠢 - 但我有大量 C 风格的 printf 代码要维护。

      我们可以使用 boost 的格式化库来重建它,也许我们会在这些日子里得到它。然而,与此同时,仅仅能够区分一个参数,或者一个 + 一个或多个参数是向前迈出的一大步。

      https://stackoverflow.com/users/365496/bames53 指出这样做是可能的,而且它似乎可以工作(可能会导致代码膨胀,但需要注意的是,这仍然是 printf 领域的所有陷阱)。

      这是一个在 MFC/C++ 中完成工作的简单示例:

      bool Write(const wchar_t * pszText);
      template <typename T> bool Write(const wchar_t * pszFormat, T, ...);
      

      Write 不需要(也不应该)调用等效的 vwsprintf,而 Write 这样做是为了在将输出字符串传递给 Write 之前构建输出字符串。

      非常优雅。消除了仅提供第二个接口的问题(如果您的一个字符串碰巧有一个意外的 printf 格式说明符,则会出现 printf 问题),或者强制客户端指定 Write() 与 WriteFormat() 或类似地执行在调用 Write() 之前本地构造字符串。

      Here's Write 根据 Write 定义:

      template <typename T> bool SimpleTextFile::Write(const wchar_t * pszFormat, T, ...)
      {
          va_list arglist;
          va_start(arglist, pszFormat);
          CStringW buffer;
          buffer.FormatV(pszFormat, arglist);
          va_end(arglist);
          return Write(buffer);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多