Bul*_*net 23 c++ language-lawyer c++11
我正在尝试移植一些为 GCC (8.2) 编写的代码以供 Clang 编译:
#include <tuple>
struct Q{};
using TUP = std::tuple<Q>;
template<typename Fn>
inline
void feh(Fn&, const std::tuple<>*)
{}
template<typename Fn, typename H>
inline
void feh(Fn& fn, const std::tuple<H>*)
{
fn(H{});
}
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
template<typename Tuple, typename Fn>
inline
void fe(Fn& fn, const Tuple * tpl = nullptr)
{
feh(fn, tpl);
}
int main()
{
auto r = [] (Q const&) {};
TUP tup;
fe<TUP>(r, &tup);
}
Run Code Online (Sandbox Code Playgroud)
GCC 8.2(和 12.1)可以很好地编译代码。fe然而,Clang 11.0.0(和 14.0.0)抱怨to的调用在和feh之间不明确。void feh(Fn& fn, const std::tuple<H>*) [with Fn = (lambda at <source>:38:14), H = Q]void feh(Fn& fn, const std::tuple<H, R...>*) [with Fn = (lambda at <source>:38:14), H = Q, R = <>]
https://godbolt.org/z/5E9M6a5c6
哪个编译器是正确的?
我怎样才能编写这段代码以便两个编译器都接受它?
和 折叠表达式都if constexpr可以在 C++17 中使用,但这是许多项目包含的库头,并且并非所有项目都是使用 C++17 编译的。我需要一个适用于 C++11 的解决方案。
use*_*570 17
哪个编译器是正确的?
Clang 拒绝代码是错误的,因为第一个重载候选者feh(Fn& fn, const std::tuple<H>*)应该优先于另一个候选者feh(Fn& fn, const std::tuple<H, R...>*),因为前者比后者更专业。
换句话说,没有包的版本被认为更专业,因此如果它与调用匹配,则应该是首选。
这是因为,基本上(大致)一个函数模板被认为比另一个函数模板更专业,后者应该能够接受前者可以接受的所有模板参数,但反之则不然。
现在,在您给定的示例中,重载feh(Fn& fn, const std::tuple<H, R...>*)可以接受(或使用)前者feh(Fn& fn, const std::tuple<H>*)可以接受的所有模板参数,但反之则不然。因此,前者比后者更专业。有关此过程的更多技术细节,请参阅模板推导中的部分排序过程是什么或来自[temp.deduct.partial]/10,其中指出:
如果对于用于确定排序的每对类型,F 中的类型至少与 G 中的类型一样专业,则函数模板 F 至少与函数模板 G一样专业
F。as且至少不像 那样专业化GFGGF。
(强调我的)
Ted*_*gmo 11
我不确定哪个编译器是正确的,但是......clang++是正确的,因为两个函数匹配得同样好。
C++11 解决方案可能只是添加该Rest部分必须至少包含一种类型的要求,并且只需添加R1. 这意味着其余代码可以保持不变:
template<typename Fn, typename H, typename R1, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R1, R...>*)
{
fn(H{});
using Rest = const std::tuple<R1, R...>*;
feh<Fn, R1, R...>(fn, static_cast<Rest>(nullptr));
}
Run Code Online (Sandbox Code Playgroud)
C++17 解决方案是删除其他feh重载并使用折叠表达式:
template <typename Fn, typename... H>
inline void feh(Fn& fn, const std::tuple<H...>*) {
(..., fn(H{}));
}
Run Code Online (Sandbox Code Playgroud)
这是逗号运算符上的一元左折叠,“展开”后变为:
(((fn(H1{}), fn(H2{})), ...), fn(Hn{}))
Run Code Online (Sandbox Code Playgroud)
到目前为止,最简单的解决方案是if constexpr:
template<typename Fn, typename H, typename... R>
inline
void feh(Fn& fn, const std::tuple<H, R...>*)
{
fn(H{});
if constexpr (sizeof...(R) > 0) {
using Rest = const std::tuple<R...>*;
feh<Fn, R...>(fn, static_cast<Rest>(nullptr));
}
}
Run Code Online (Sandbox Code Playgroud)
并删除有问题的过载。
| 归档时间: |
|
| 查看次数: |
1701 次 |
| 最近记录: |