验证模板类型的可调用签名

C++ verify callable signature of template type

本文关键字:调用 类型 验证      更新时间:2023-10-16

假设我有一个这样的通用函数:

template<typename TFunc>
void templFunc(TFunc func) {
    func(3, 6);
}

在编译时是否有任何方法可以验证TFunc的签名,无论它是std::函数还是lambda或任何类型的函数引用。我只是想确保TFunc是签名void(int, int)或类似于static_assert,所以我可以产生一个非垃圾错误消息。

所以我摆弄了一些type_traits的东西,我想我有一些东西来验证整个签名,而不仅仅是返回值,并允许您创建易于阅读的static_断言,而不是当签名不匹配时难以辨认的模板错误。这是一个糟糕的解决方案吗?

#include <functional>
template<typename, typename, typename = void>
struct is_signature : std::false_type {};
template<typename TFunc, typename Ret, typename... Args>
struct is_signature<TFunc, Ret(Args...),
        typename std::enable_if<
            std::is_convertible<
                TFunc,
                std::function<Ret(Args...)>
            >::value
        >::type
    > : public std::true_type
    {};
// works on both functions and lambda's
void blah(int, int) {
}
template<typename TFunc>
void templFunc(TFunc func) {
    static_assert(is_signature<TFunc, void(int, int)>::value, "Not gonna work! more info follows:");
    func(3, 6);
}
int main() {
    auto b = [](int, int) -> void {
    };
    auto c = [](int) -> void {
    };
    static_assert(is_signature<decltype(b), void(int, int)>::value, "b convertible to a std::function<void(int, int), so this checks out!");
    static_assert(is_signature<decltype(b), void(int)>::value, "b not convertible to a std::function<void(int)>, so this will error in compilation.");
    static_assert(is_signature<decltype(blah), void(int, int)>::value, "blah convertible to a std::function<void(int, int), so this checks out!");
    static_assert(is_signature<decltype(blah), void(int)>::value, "blah not convertible to a std::function<void(int)>, so this will error in compilation.");
    templFunc(b); // <- ok
    templFunc(c); // <- static assertion : not gonna work!
    return 0;
}

您可以使用:

template<typename TFunc>
void templFunc(TFunc func) {
   static_assert(std::is_void<decltype(func(0,0))>::value,
                 "Bad template argument. The return type is not void");
   func(3, 6);
}
  1. 这将确保函数的返回类型为void
  2. 如果函数不接受两个参数,它将在调用func(3,6);时失败——两次。一次在static_assert行,一次在下一行

  3. 如果intint可以提升、转换或强制转换为的任何其他类型的参数类型,则函数调用将成功。确保参数类型仅为int将需要一些额外的工作。

测试程序:

#include <type_traits>
template<typename TFunc>
void templFunc(TFunc func) {
   static_assert(std::is_void<decltype(func(0,0))>::value, "Bad template argument. The return type is not void");
   func(3, 6);
}
void foo()
{
}
void bar(int x, int y)
{
}
int baz(int x, int y)
{
   return 0;
}
struct Functor
{
   void operator()(long, long)
   {
   }
};
int main()
{
   templFunc(foo);    // Not OK. Two few arguments
   templFunc(bar);    // OK
   templFunc(baz);    // Not OK. Wrong return type
   templFunc([](int, int) -> void {} );  // OK
   templFunc(Functor());   // OK
}

类似的东西使用SFINAE工作(仅当您基于模板参数进行断言;我不太确定为什么,我认为这将是最有趣的部分:):)

#include <type_traits>
template<typename TFunc>
typename std::enable_if<std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value >::type templFunc(TFunc func)
{
  func(3, 6);
}
template<typename TFunc>
typename std::enable_if<!std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value >::type templFunc(TFunc func)
{
  static_assert(std::is_same<typename std::result_of<TFunc(int, int)>::type, void>::value, "error; invalid function");
}
auto a = [](int, int) {};
auto b = [](int x, int y) { return x + y; };
int main()
{
    templFunc(b);
    return 0;
}