检查 class 是否有带有特定模板的 operator() 的方法

The way to check that class has operator() with specific template

我正在尝试使用 SFINAE 技术来检查 - class/struct 是否具有 operator() 以及特定的模板声明,例如(略微简化):

struct Simplified
{
  //  1st operator - I need to detect its presence
  template<typename T, int I>
  void operator()(T& val1, double val2)
  {
    std::cout << std::to_string(val1 + val2) << std::to_string(I) << std::endl;
  }
    
  //  2nd operator
  void operator()(double& val1, double val2)
  {
    std::cout << std::to_string(val1 + val2) << std::endl;
  }
};

而且我无法检查 operator() 模板化签名。这是我的帮手 class:

template<typename Fn, int I, typename T>
class TemplatedOperatorCheck
{
  struct Exists { };
  struct NotExists { };

  template<typename U, void(U::*)(T&, double)> struct SFINAESimple {};
  template<typename U> static Exists Test(SFINAESimple<U, &(U::operator())>*);
  // The problem is probably there                         ^^^^^^^^^^^^^^^
  template<typename U> static NotExists Test(...);
public:
  static constexpr bool kExists = std::is_same<decltype(Test<Fn>(nullptr)), Exists>::value;
};

但是这个class只能检测到非模板operator()的存在。

// true if 2nd operator declared in Simplified class, false otherwise.
auto DoubleOpValid = TemplatedOperatorCheck<Simplified, 0, double>::kExists;

// always false
auto IntOpValid = TemplatedOperatorCheck<Simplified, 0, int>::kExists;

我做错了什么?在 Google 或 Whosebug 找不到任何相关内容...

提前致谢!任何帮助将不胜感激。

嗯...不确定您到底想要什么但是...我提出以下建议 class

template<typename Fn, int I, typename T>
class TemplatedOperatorCheck
 {
   template<typename U, void(U::*)(T&, double)>
   struct SFINAESimple
    { };

   template<typename U>
   static std::true_type Test(SFINAESimple<U, &U::operator()>*, int);

   template<typename U>
   static std::true_type Test(SFINAESimple<U, &U::template operator()<T, I>>*, long);

   template<typename U>
   static std::false_type Test(...);

   public:
      static constexpr bool kExists = decltype(Test<Fn>(nullptr, 0))::value;
 };

一些观察,没有特别的顺序...

  1. 您可以使用std::true_typestd::false_type,而不是ExistNotExist;因此 kExists 初始化得到简化,不再需要 std::is_same

  2. 第二个模板参数调用 SFINAESimple 的正确语法是没有括号。我的意思是:&U::operator(),不是&(U::operator())

  3. 您的 Test() 函数,将模板参数 &U::operator() 传递给 SFINAESimple,适用于 non-template 函数;如果你希望它也适用于模板函数,你必须从可能的函数集中生成所需的函数,所以我添加了另一个传递 &U::template operator()<T, I> 参数的 Test() 函数

  4. 鉴于 class 可以同时具有模板和 non-template 函数,编译器 select 两个 Test() 函数返回 std::true并给出一个错误,因为它不知道哪个更喜欢;为了允许编译器更喜欢一个版本或另一个版本,我添加了另一个参数(一个是 int,另一个是 long),因此调用 Test<Fn>(nullptr, 0),编译器更喜欢 int版本(因为0,第二个参数,是一个int),避免歧义,避免错误。

下面是一个完整的编译示例

#include <iostream>

struct Simplified
{
  //  1st operator - I need to detect its presence
  template<typename T, int I>
  void operator()(T & val1, double val2)
  {
    std::cout << std::to_string(val1 + val2) << std::to_string(I) << std::endl;
  }
    
  //  2nd operator
  void operator()(double & val1, double val2)
  {
    std::cout << std::to_string(val1 + val2) << std::endl;
  }
};

template<typename Fn, int I, typename T>
class TemplatedOperatorCheck
 {
   template<typename U, void(U::*)(T&, double)>
   struct SFINAESimple
    { };

   template<typename U>
   static std::true_type Test(SFINAESimple<U, &U::operator()>*, int);

   template<typename U>
   static std::true_type Test(SFINAESimple<U, &U::template operator()<T, I>>*, long);

   template<typename U>
   static std::false_type Test(...);

   public:
      static constexpr bool kExists = decltype(Test<Fn>(nullptr, 0))::value;
 };

int main()
 {
   auto DoubleOpValid = TemplatedOperatorCheck<Simplified, 0, double>::kExists;

   auto IntOpValid = TemplatedOperatorCheck<Simplified, 0, int>::kExists;

   std::cout << DoubleOpValid << ' ' << IntOpValid << '\n';
 }