我试图移植一些为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);
}
GCC 8.2(和12.1)很好地编译了代码。然而,Clang 11.0.0(和14.0.0)抱怨说,从fe到feh的调用在无效feh之间不明确(Fn
https://godbolt.org/z/5E9M6a5c6
哪个编译器是正确的?
如何编写这段代码以便两个编译器都接受它?
这两个if constexr
和折叠表达式都可以在C 17中使用,但这是许多项目中包含的库标头,并非所有项目都使用C 17编译。我需要一个在C 11中工作的解决方案。
到目前为止,最简单的解决方案是if constexr
:
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));
}
}
只需移除有问题的重载即可。
<罢工> 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));
}
C 17解决方案是删除其他feh重载并使用折叠表达式:
template <typename Fn, typename... H>
inline void feh(Fn& fn, const std::tuple<H...>*) {
(..., fn(H{}));
}
这是逗号运算符的一元左折,“展开”为:
(((fn(H1{}), fn(H2{})), ...), fn(Hn{}))
哪个编译器是正确的?
我认为clang拒绝代码是错误的,因为第一个重载候选feh(Fn
换句话说,不带包的版本被认为更专业,因此如果它与调用匹配,则应首选该版本。
这是因为,基本上(大致上)对于一个被认为比另一个更专业的函数模板,后者应该能够接受前者可以接受的所有模板参数,但反之亦然。
现在,在您给定的示例中,重载
feh(Fn
在平局的情况下,如果一个函数模板有一个尾随参数包,而另一个没有,则具有省略参数的函数模板被视为比具有空参数包的函数模板更专业化。
(重点矿山)
假设我们有以下简单的代码: 这段代码使用clang编译并打印“T”,但使用gcc我们有以下错误: 我的问题是哪个编译器有bug,gcc还是叮当声?
请考虑以下人为的代码段: 如果第二个重载被更改为采用至少2种类型的包而不是至少一种类型的包时,gcc和clang都接受这两个调用: 如果未推导出的模板参数被移动到推导出的模板参数,则: 我希望每个版本的所有调用都可以。上面代码示例的预期结果是什么?
对于下面的代码,我在gcc中得到了一个模糊的模板实例化错误。但是,使用Clang或Visual Studio,代码可以很好地编译。可以在此处找到代码的完整工作示例:http://coliru.stacked-crooked.com/a/60ef9d73ce95e6f9 我有一个类模板,它是从一个聚合类型构建的 聚合类型由一系列基类组成,例如 我定义了MyClass的两个专业。第一个专业化是常见情况
模板函数与重载是密切相关的。从函数模板产生的相关函数都是同名的,因此编译器用重载的解决方法调用相应函数。 函数模板本身可以用多种方式重载。我们可以提供其他函数模板,指定不同参数的相同函数名。例如,图12.2的printArray函数模板可以用另一printArray函数模板重载,用参数lowSubscriPt和highSubscript指定要打印的数组部分(见练习12.4)。 函数模板也可以用其他
是否可以编写一个模板,根据是否在类上定义了某个成员函数来改变行为? 下面是我想写的一个简单的例子: 因此,如果定义了,那么它将使用它;否则就不会。我不知道怎么做的神奇部分是“function_exists”部分。
bool template_exists(string template) This function checks whether the specified template exists. It can accept either a path to the template on the filesystem or a resource string specifying the temp