正在检查可调用的模板参数类型
Checking callable template parameter types
编辑: 问题中概述的方法存在问题,原因如下。最后,我用不同的方式解决了这个问题,见下面的答案
我有一些模板类,其中的模板参数应该是与某个签名匹配的可调用参数。如果用户提供的模板参数要么不可调用,要么与预期签名不匹配,那么编译将在回调机制内部失败,由此产生的错误消息很难解密。相反,如果给定的模板参数无效,我希望能够使用static_assert
提前提供一个好的、易于理解的错误消息。不幸的是,这似乎很难做到
我使用以下设置:
#include <type_traits>
namespace detail {
template <typename Function, typename Sig>
struct check_function
{
static constexpr bool value =
std::is_convertible<Function, typename std::decay<Sig>::type>::value;
};
template <typename, typename>
struct check_functor;
template <typename Functor, typename Ret, typename... Args>
struct check_functor<Functor, Ret(Args...)>
{
typedef Ret (Functor::*Memfn) (Args...);
static constexpr bool value =
check_function<decltype(&Functor::operator()), Memfn>::value;
};
} // end namespace detail
template <typename Func, typename Sig>
struct check_function_signature
{
using Type =
typename std::conditional<
std::is_function<Func>::value,
detail::check_function<Func, Sig>,
detail::check_functor<Func, Sig>>::type;
static constexpr bool value = Type::value;
};
即,如果Func
是函数指针类型,则将其直接与所需签名进行比较,否则将其假定为函子,并将其operator()
进行比较。
这似乎适用于简单函数和用户定义的函数,但出于某种原因,我无法理解它在lambdas(用Clang 3.4和g++4.8测试)中失败:
int f(int i);
struct g
{
int operator() (int i) { return i; }
};
int main()
{
static_assert(check_function_signature<decltype(f), int(int)>::value,
"Fine");
static_assert(check_function_signature<g, int(int)>::value,
"Also fine");
auto l = [](int i) { return i; };
static_assert(check_function_signature<decltype(l), int(int)>::value,
"Fails");
}
我的理解是,标准要求lambdas被实现为与上面的g
等价的匿名函子,所以我不明白为什么前者有效,而后者无效。
总之,我的问题是:
- 我在这里使用的方法真的合理吗,还是我犯了一个明显的错误
- 为什么这似乎适用于用户定义的函子,但对于编译器定义的函函数(即lambdas)却失败了
- 是否有一个修复/解决方法可以用这种方式检查Lambda
- 我对这个代码还有其他改进吗?(可能很多…)
提前感谢,这已经突破了我的模板元编程知识的极限,所以任何建议都将不胜感激。
(回答我自己的问题,因为我找到了更好的方法来实现我想要的,并认为我会分享它。)
根据这些响应,特别是remaybel评论中的链接答案,我最终得到了大量的代码,这些代码从函子的operator()
中剥离了类类型,并根据所需的签名检查了每个参数类型。然而,这并不能很好地工作,因为获得指向T::operator()
的成员指针的要求意味着,如果operator()
有多个重载,或者它被定义为模板,它就会失败。我也不确定它在所有情况下都能正确处理参数转换,而且有很多事情很难纠正。
经过进一步思考,我意识到我真正想做的是尝试用我所需的参数类型构造一个函数调用,如果不能进行这样的调用,就会失败。后来进行了一点黑客攻击,我想出了这个:
template <typename, typename, typename = void>
struct check_signature : std::false_type {};
template <typename Func, typename Ret, typename... Args>
struct check_signature<Func, Ret(Args...),
typename std::enable_if<
std::is_convertible<
decltype(std::declval<Func>()(std::declval<Args>()...)),
Ret
>::value, void>::type>
: std::true_type {};
这将使用declval
为可调用对象本身和参数构造一个"伪"函数调用,并检查结果是否可以转换为所需的类型。如果这样的呼吁无效,SFINAE将介入,部分专业化将被拒绝。
这比我以前想做的要短得多,(IMO)也优雅得多。它对我试图抛出的每一个可调用项都能正常工作
尽管如此,正如我在最初的问题中所说,这已经突破了我元编程知识的极限,所以如果有任何关于如何改进这段代码的建议,请告诉我。
您在operator ()
中缺少const
说明符。
带有:
template <typename Functor, typename Ret, typename... Args>
struct check_functor<Functor, Ret(Args...)>
{
typedef Ret (Functor::*Memfn) (Args...) const; // const added here
static constexpr bool value =
check_function<decltype(&Functor::operator()), Memfn>::value;
};
检查对于(不可变的)lambda是正确的(但对于您的自定义可变函子不是正确的)。否则,您必须使lambda可变:
auto l = [](int i) mutable { return i; };
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型