神奇的VS
环境一: Windows 10 + Visual Stuido 2017 社区版 + C++标准最新草案
(笑话,对C++17的支持少的可怜,is_same_v
都有了却不支持inline variable
。。。)
环境二: FreeBSD 11 + Clang 4.0 + -std=c++17
环境三: openSUSE Tumbleweed + Clang 4.0 (-std=c++1y
) + GCC 7.0.1 (-std=c++17
)
如你所见,这一次VS又傻逼了,下面是“正常的”代码
#include <type_traits>
template<typename F, typename... Args>
struct have_same_args
{
private:
template<typename Func>
static std::false_type test(...);
template<typename Func, typename = decltype(std::declval<F>()(std::declval<Args>()...))>
static std::true_type test(int);
public:
constexpr static bool value = decltype(test<F>(0))::value;
};
template<typename> struct check;
template<typename Res, typename... Args>
struct check<Res(Args...)>
{
private:
using Sig = Res(Args...);
public:
template<typename R, typename... FArgs>
constexpr static bool is_same(R, FArgs...)
{
return std::is_same<Res, R>::value && have_same_args<Sig, FArgs...>::value;
}
};
int main()
{
static_assert(check<int(int, const char*)>::is_same(1, 1, 2.33), "not same");
}
一个是模板函数,一个是非模板的C风格的变参函数,显示调用模板函数,根据匹配规则,vs调用模板函数test
,但是似乎忽略了默认模板参数的推导,而GCC和Clang在推导默认模板参数的时候发现函数签名不一致,于是报错退出了,并不是因为static_assert
失败,而上面的代码才会导致static_assert
失败,并且同样会出现函数签名不一致的报错。
无论如何,上面的代码都不是最终想要的,这个才是!
对于类成员函数相当于做了remove_cv
,对于λ函数,相当于是匿名类+operator()而已,和成员函数没差多少
#include <functional>
#include <type_traits>
#include <tuple>
template<typename, typename> struct is_match;
template<typename F, typename Res, typename... Args>
struct is_match<F, Res(Args...)>
{
private:
template<typename Func> struct extract;
template<typename R, typename... A>
struct extract<R(A...)>
{
using ires = R;
using iargs = std::tuple<A...>;
};
template<typename R, typename... A>
struct extract<R(*)(A...)> : public extract<R(A...)> {};
template<typename R, typename... A>
struct extract<std::function<R(A...)>> : public extract<R(A...)> {};
template<typename R, typename Class, typename... A>
struct extract<R(Class::*)(A...)> : public extract<R(A...)> {};
template<typename R, typename Class, typename... A>
struct extract<R(Class::*)(A...) const> : public extract<R(A...)> {};
template<typename R, typename Class, typename... A>
struct extract<R(Class::*)(A...) volatile> : public extract<R(A...)> {};
template<typename R, typename Class, typename... A>
struct extract<R(Class::*)(A...) const volatile> : public extract<R(A...)> {};
// immutable lambda call <R(::*)(A..) const>
template<typename lambda>
struct extract : public extract<decltype(&lambda::operator())> {};
using res = Res;
using args = std::tuple<Args...>;
using tmp = extract<F>;
using ires = typename tmp::ires;
using iargs = typename tmp::iargs;
public:
constexpr static bool ret_same = std::is_same<res, ires>::value;
constexpr static bool args_same = std::is_same<args, iargs>::value;
constexpr static bool same = ret_same && args_same;
};
int foo(bool, const char*) { return 0; }
class Foo
{
public:
int foo(int, const char*) { return 0; }
int operator()(int, const char*) { return 0; }
static int bar(int, const char*) { return 0; }
};
int main()
{
using type = int(int, const char*);
auto bar = [](int, const char*) -> int { return 0; };
static_assert(is_match<decltype(bar), type>::same, "not same");
static_assert(is_match<decltype(&Foo::foo), type>::same, "not same");
static_assert(is_match<decltype(&Foo::operator()), type>::same, "not same");
static_assert(is_match<decltype(&Foo::bar), type>::same, "not same");
static_assert(is_match<std::function<type>, type>::same, "not same");
static_assert(is_match<decltype(foo), type>::same,
"***return type or arguments type not same***");
}