![](/img/trans.png)
[英]g++ and clang different behavior with recursive variadic template bitset creation (possible gcc bug?)
[英]Clang and GCC different behavior when resolving variadic function template overload
考虑以下代码:
#include <utility>
int foo_i(int x) { return x + 1; }
char foo_c(char x) { return x + 1; }
using II = int (*)(int);
using CC = char (*)(char);
template<typename F>
struct fn {
F f;
template<typename... Args>
decltype(auto) operator()(Args&&... args) const
{
return f(std::forward<Args>(args)...);
}
};
struct fn_2 : private fn<II>, private fn<CC> {
fn_2(II fp1, CC fp2)
: fn<II>{fp1}
, fn<CC>{fp2}
{}
using fn<II>::operator();
using fn<CC>::operator();
};
int main()
{
fn_2 f(foo_i, foo_c);
f(42);
}
基本上, fn<T>
存储类型为T
的仿函数(不一定是函数指针),其可变参数operator()
将所有内容转发给仿函数。
这段代码用gcc 4.9.2到gcc 6.1很好地编译,但被我试过的每个clang版本都拒绝了,甚至是clang 3.8 。 clang抱怨说这个电话很模糊。 (如果有人可以尝试使用VS编译它,我会很感激,因为我现在无法访问它。)
哪种编译器是正确的,我如何解决这种差异?
更新:虽然我仍然不确定哪个编译器的行为(更多)符合标准,但我找到了一个解决方法:在指向函数的指针上专门化fn<T>
,并避免盲目使用variadic operator()
。 (好吧,我们仍然没有指出成员函数的指针......现在我将忽略它们。:/) 示例 :
template<typename F>
struct fn : private F {
using F::operator();
};
template<typename R, typename... Args>
struct fn<R (*)(Args...)> {
fn(R (*f)(Args...)) noexcept : f_(f) {}
R operator()(Args&&... args) const
{
return f_(std::forward<Args>(args)...);
}
private:
R (*f_)(Args...);
};
我认为clang
就在这里不编译代码,因为operator()
显然是模棱两可的。 如果你考虑一下,从提供的operator()
模板签名中,不清楚应该首选哪个函数。 您必须根据fn
存储的函数为编译器提供其他提示。
这是我的解决方案:
#include <utility>
#include <type_traits>
#include <iostream>
int foo(int x) { return x + 1; }
char foo(char x) { return x + 1; }
using II = int (*)(int);
using CC = char (*)(char);
template <bool... B>
struct bool_pack {};
template <bool... V>
using all_true = std::is_same<bool_pack<true, V...>, bool_pack<V..., true>>;
template <typename... Args> struct packed {};
template <typename T> struct func_traits;
template <typename R, typename... Args>
struct func_traits<R(*)(Args...)> {
using type = packed<Args...>;
};
template<typename F>
struct fn {
F f;
template<typename... Args,
typename std::enable_if<std::is_same<packed<Args...>, typename func_traits<F>::type>::value>::type* = nullptr>
auto operator()(Args&&... args) const
{
return f(std::forward<Args>(args)...);
}
};
struct fn_2 : private fn<II>, private fn<CC> {
fn_2(II fp1, CC fp2)
: fn<II>{fp1}
, fn<CC>{fp2}
{}
using fn<II>::operator();
using fn<CC>::operator();
};
int main()
{
fn_2 f(static_cast<II>(foo),
static_cast<CC>(foo));
std::cout << f(42) << std::endl;
std::cout << f('a') << std::endl;
}
没什么好看的,但是我使用enable_if
来帮助编译器根据存储函数的arity类型选择正确版本的operator()
。
这是一个GCC错误。 请注意,即使使用char
类型的参数调用,GCC也始终调用fn<II>
版本。 编译器无法确定要调用哪个函数模板,因为它们具有完全相同的签名,而GCC只是随意选择一个。
如果char
和int
是没有隐式转换的独立类型,那么代码将完美地运行。 但是,由于char
和int
可以在彼此之间隐式转换(是的, int
到char
可以隐式转换!),调用中可能存在歧义。 如果存在,GCC在选择不需要转换的呼叫时会做出直观的例外。
编辑:我已经指出有一个模板化的参数,在这里获得自己的operator()函数。 这是我绝对没有看到的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.