检测是否存在具有给定签名的功能

时间:2019-12-10 20:39:11

标签: c++ c++17 variadic-templates template-meta-programming typetraits

我正在尝试使用模板元编程来查找具有特定签名的函数(而非类方法)。为此,我想使用detection idiom

假设我具有函数foo()bar()

void foo(int x);

template<typename... Ts>
void bar(Ts&& ...args);

另一个名为foobar()的函数接收一个函数和一个参数包。 foobar()检查是否可以通过通过参数包接收的参数调用接收到的函数:

template<typename F, typename... Ts>
void foobar(F&& fun, Ts&& ...args) {
    if constexpr(func_with_signature_exists_v<fun(declval<Args>()...)>)
        do_something;
    }

我期望if中的foobar()语句产生以下结果:

if constexpr(func_with_signature_exists_v<foo(int)>) // true
if constexpr(func_with_signature_exists_v<foo(float)>) // false
if constexpr(func_with_signature_exists_v<foo(int, int)>) // false
if constexpr(func_with_signature_exists_v<bar(int)>) // true
if constexpr(func_with_signature_exists_v<bar(int, int)>) // true
if constexpr(func_with_signature_exists_v<bar(int, float, int)>) // true

我尝试按照this链接接受的答案中的步骤进行操作,但是当我尝试将float传递给期望int的函数时,is_detected惯用法并没有抱怨。

此外,我更愿意将任何函数传递给using语句,而不是将其专门用于一个特定的函数,例如:

template<typename... Args>
using test_t = decltype(f(std::declval<Args>()...));

有什么方法可以实现我想要的?还是类似的东西?

请注意,这是针对辅助项目的,因此我尝试使用C ++ 17,而不是C ++ 14或C ++ 11。

1 个答案:

答案 0 :(得分:1)

foo()bar()带来了完全不同的问题。

首先:bar(),这是一个模板函数。

假设您希望bar()是模板函数(如果是operator() / class的模板struct,则为模板函数)

static_assert(foobar(bar, 1));

我能想象的最好的是,foobar()应该是C风格的宏;和可变参数的,使事情变得更复杂(但不要让我开发该宏)。

这是因为您不能将 template 函数作为函数参数传递,因为template函数不是对象而是一组对象。

对于foo(),如果我理解正确,那么问题在于,当自变量可转换为函数自变量时,检测惯用语就为真

static_assert(foobar(foo, 3.4)); // you get true but you want false

,并且您要将函数作为参数传递。

这很容易解决(但我认为不是很有用)。

如果您声明(不需要定义)如下所示的几个重载函数

template <typename ... Args, typename R>
std::true_type fwse (R(*)(Args...), int);

template <typename ..., typename T>
std::false_type fwse (T, long);

和一个constexpr可变参数模板变量

template <typename T, typename ... Args>
constexpr auto func_with_signature_exists_v
   = decltype(fwse<Args...>(std::declval<T>(), 0))::value;

您还可以按如下方式编写foobar()

template <typename F, typename ... Ts>
constexpr auto foobar (F, Ts const & ...)
 {
   if constexpr ( func_with_signature_exists_v<F, Ts...> )
      return std::true_type{};
   else
      return std::false_type{};
 }

您会得到

static_assert( foobar(foo, 7) == true );
static_assert( foobar(foo, 3.4) == false );
static_assert( foobar(foo, 1, 2) == false );

我认为这不是很有用,因为当foo()参数的类型必须是普通类型(没有const,没有引用)时,这会起作用。

如果可以避免将值传递给foobar(),并且可以直接传递Args...类型,则可以

static_assert( foobar<int>(foo) == true );
static_assert( foobar<double>(foo) == false );
static_assert( foobar<int, int>(foo) == false );

您可以按以下方式重写foobar()

template <typename ... Ts, typename F>
constexpr auto foobar (F)
 {
   if constexpr ( func_with_signature_exists_v<F, Ts...> )
      return std::true_type{};
   else
      return std::false_type{};
 }

并变得更加灵活(因为也可以接受const和/或Ts...的引用类型)。

以下是完整的编译示例

#include <type_traits>

void foo(int x)
 { }

template <typename ... Args, typename R>
std::true_type fwse (R(*)(Args...), int);

template <typename ..., typename T>
std::false_type fwse (T, long);

template <typename T, typename ... Args>
constexpr auto func_with_signature_exists_v
   = decltype(fwse<Args...>(std::declval<T>(), 0))::value;

template <typename ... Ts, typename F>
constexpr auto foobar (F)
 {
   if constexpr ( func_with_signature_exists_v<F, Ts...> )
      return std::true_type{};
   else
      return std::false_type{};
 }

int main ()
 {
   static_assert( foobar<int>(foo) == true );
   static_assert( foobar<double>(foo) == false );
   static_assert( foobar<int, int>(foo) == false );
 }
相关问题