我正在尝试创建一个特征,以检测带有2个参数调用的类型Apply的结果。我希望代码中的static_assert不会被击中,因为apply的结果是有效的(除法浮点数)。为什么会断言此命中,以及如何将特征更改为将所有适用的有效重载检测为true_type或constexpr bool true呢?
#include <type_traits>
#include <functional>
struct Apply
{
template<typename T1, typename T2>
float apply(const T1& a, const T2& b) const
{
return a / b;
}
};
struct ApplyInvoker
{
Apply a;
template<typename... Args>
auto operator()(Args&&... args)
{
return a.apply(std::forward<Args>(args)...);
}
};
template <class Void, class... T>
struct ValidCall : std::false_type
{
};
template <class... T>
struct ValidCall<
std::void_t<decltype(std::invoke(std::declval<ApplyInvoker>(), std::declval<T>()...))>,
T...>
: std::true_type
{
};
template<typename T1, typename T2>
constexpr bool CanApply = ValidCall<void, T1, T2>::value;
int main()
{
static_assert(CanApply<float, float>);
}
您将模板用作:
template<typename T1, typename T2>
constexpr bool CanApply = ValidCall<T1, T2>::value;
但是主变量声明为:
template <class Void, class... T>
struct ValidCall;
该第一个模板参数被命名为Void
,因为它已经为void
-这是专业化所要匹配的。所以你必须这样做:
template<typename T1, typename T2>
constexpr bool CanApply = ValidCall<void, T1, T2>::value;
// ^~~~
而且也是C ++ 17,为此,我们具有类型特征:
template<typename T1, typename T2>
constexpr bool CanApply = std::is_invocable_v<ApplyInvoker, T1, T2>;
问题是Apply
和ApplyInvoker
都无法真正使用类型特征。 Apply
宣称自己可以被任何两个参数调用-否则无法检测。 ApplyInvoker
宣传自己可以使用任意数量的参数,但是如果您尝试使用错误的参数集,这种方式将导致编译器出现错误。这两种类型都是我们称之为SFINAE不友好的类型-它们对特征类型不友好,无法测试。
如果您希望它们实际上是可测试的,那么您需要按如下方式重写它们:
struct Apply
{
template<typename T1, typename T2>
float apply(const T1& a, const T2& b) const
-> decltype(float(a / b))
{
return a / b;
}
};
struct ApplyInvoker
{
Apply a;
template <typename... Args>
auto operator()(Args&&... args)
-> decltype(a.apply(std::forward<Args>(args)...)
{
return a.apply(std::forward<Args>(args)...);
}
};
或类似的东西。