【问题标题】:Mysterious C++ variadic template expansion神秘的 C++ 可变参数模板扩展
【发布时间】:2021-10-20 22:46:38
【问题描述】:

以下 C++ 函数摘自第 151 - 157 行here

template <typename... T>
std::string JoinPaths(T const&... paths) {
  boost::filesystem::path result;
  int unpack[]{0, (result = result / boost::filesystem::path(paths), 0)...};
  static_cast<void>(unpack);
  return result.string();
}

函数JoinPaths("foo", "bar", "doo.txt") 将返回一个std::string"foo/bar/doo.txt"(我认为)所以我理解函数的语义。

我试图理解return 语句之前的两行。 unpackints 的数组中,但是前面的 0 和末尾的省略号发生了什么(以及为什么)。有人可以解释这是如何扩展的吗?为什么是0?我假设static_cast 是为了防止编译器优化数组?

【问题讨论】:

  • 扩展的哪一部分不明白?也许this question 解释一下?
  • @wcochran,不,只是为了抑制有关未使用变量(在这种情况下为数组)的警告。
  • 顺便说一句,您可能还想阅读this。 tldr:您发布的代码是 C++17 之前的解决方法,因为该语言中缺少折叠表达式。
  • @wcochran 我起初误解了这个问题并删除了评论。是的,第二个0 只是在(something, 0) 中丢弃something 并让结果为int。第一个0 是不要尝试创建一个大小为零的数组,以防万一调用没有参数的函数
  • 发布 C++17 你可以折叠一个逗号运算符表达式对吗?即(result = result / boost::filesystem::path(paths), ... )?

标签: c++ variadic-templates


【解决方案1】:

正如我在输入此内容时已经指出的那样,它是执行 fold expression 的 C++17 之前的方法。

它使用 int 的本地临时数组以及逗号运算符来偷偷摸摸一堆本来应该是的东西:

result = result / paths[n]

转化为整数表达式,最终产生:

int unpack[]{
  0, 
  (result = result / paths[0], 0),
  (result = result / paths[1], 0),
  ...,
  (result = result / paths[N], 0)
};

这些天来,以下是可行的:

template <typename...Pathnames>
std::string JoinPaths( Pathnames...pathnames )
{
  return (std::filesystem::path( pathnames ) / ...).string();
}

【讨论】:

  • 谢谢 ... 值得一提的是 0 的两种用途——一种用于避免空数组,另一种用于逗号运算符,以便产生 int 类型的结果...跨度>
【解决方案2】:
int unpack[]{0, (result = result / boost::filesystem::path(paths), 0)...};

第一个0 用于在有人使用零参数调用函数时不尝试创建空数组。

(result = result / boost::filesystem::path(paths), 0)

这会评估 result = result / boost::filesystem::path(paths) 并丢弃它。结果是逗号右侧的0

... 是一个 pack 扩展,在每个之间放置一个,,使其成为:

int unpack[]{0, (result = result / boost::filesystem::path("foo"),0), 
                (result = result / boost::filesystem::path("bar"),0),
                (result = result / boost::filesystem::path("doo.txt"),0)
            };

因此,unpack 中将有四个 0:s。之后的演员只是为了禁用关于未使用变量unpack的警告。

C++17 起:

[[maybe_unused]] int unpack ... 可用于删除警告。也可以使用 fold 表达式constexpr if 完全跳过 unpack 变量 - 并且可以使用 std::filesystem 代替 boost::filesystem

template<class... Ps>
std::string JoinPaths2(Ps&&... paths) {
    std::filesystem::path result;
    if constexpr (sizeof...(Ps)) // can't unfold empty expansion over /
        result = (std::filesystem::path(std::forward<Ps>(paths)) / ...);
    return result.string();
}

【讨论】:

  • 这听起来几乎是惯用语......我第一次遇到它。,
  • @wcochran 折叠表达式?是的,这是一个不错的功能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-04-12
  • 2014-10-30
  • 1970-01-01
  • 2013-10-03
  • 2017-05-28
  • 1970-01-01
相关资源
最近更新 更多