神秘的 C++ 可变参数模板扩展
Mysterious C++ variadic template expansion
以下 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")
将 return 一个 std::string
值 "foo/bar/doo.txt"
(我认为)所以我理解函数的语义。
我试图理解 return
语句之前的两行。 unpack
在 int
的数组中,但是前导 0 和末尾的省略号发生了什么(以及为什么)。有人可以解释这是如何扩展的吗?为什么是0?我假设 static_cast
是为了防止编译器优化数组?
正如我在输入时已经指出的那样,这是一种执行 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();
}
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
。
...
是一个 包扩展 ,在每个包之间放置一个 ,
,使其成为:
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 expression 和 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();
}
以下 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")
将 return 一个 std::string
值 "foo/bar/doo.txt"
(我认为)所以我理解函数的语义。
我试图理解 return
语句之前的两行。 unpack
在 int
的数组中,但是前导 0 和末尾的省略号发生了什么(以及为什么)。有人可以解释这是如何扩展的吗?为什么是0?我假设 static_cast
是为了防止编译器优化数组?
正如我在输入时已经指出的那样,这是一种执行 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();
}
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
。
...
是一个 包扩展 ,在每个包之间放置一个 ,
,使其成为:
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 expression 和 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();
}