【问题标题】:The way to check that class has operator() with specific template检查该类是否具有带有特定模板的 operator() 的方法
【发布时间】:2020-09-28 17:38:05
【问题描述】:

我正在尝试使用 SFINAE 技术来检查 - 类/结构是否具有带有特定模板声明的 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() 模板签名。这是我的助手类:

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;
};

但是这个类只能检测到非模板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 或 stackoverflow 中找不到任何相关内容...

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

【问题讨论】:

  • std::is_invocable_v 不会为你做这件事吗?
  • 我有点困惑...您希望测试仅检测第一个运算符,仅检测第二个运算符,还是同时检测两者?
  • @IgorG ,实际上我只需要检测第一个操作员的存在。第二个只是为了澄清不应检测到的内容。

标签: c++ templates template-meta-programming sfinae


【解决方案1】:

嗯...不确定您到底想要什么,但是...我建议以下课程

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 的第二个模板参数的正确语法不带括号。我的意思是:&amp;U::operator(),而不是&amp;(U::operator())

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

  4. 鉴于一个类可以同时具有模板函数和非模板函数,编译器会同时选择返回std::trueTest() 函数并给出错误,因为它不知道更喜欢哪一个;为了允许编译器选择一个或另一个版本,我添加了另一个参数(一个是int,另一个是long),所以调用Test&lt;Fn&gt;(nullptr, 0),编译器更喜欢int版本(因为@987654342 @,第二个参数,是一个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';
 }

【讨论】:

  • 感谢您及时详细的回复。不幸的是,我无法编译您的代码(至少在 MSVC 2019 上,既不使用 c++14 也不使用 c++17 编译器)-它声称 error C2977: 'TemplatedOperatorCheck&lt;Fn,I,T&gt;::SFINAESimple': too many template arguments 和其他错误-所有在线包含 &amp;U::template operator()&lt;T, I&gt; 。即使我在 main.cpp 中只放了 TemplatedOperatorCheck 类并且不要引用它。
  • @Ukridge - 嗯...也许您使用的 Simplified 结构与您在示例中转录的结构几乎没有区别...无论如何...添加了完整的使用 clang++ 和 g++ 编译的编译示例。
  • 我已经尝试了您的完整示例,并在 MSVC 上得到了相同的结果。是的,你的例子,用 g++ 编译,就像一个魅力。似乎 cl(Microsoft 编译器)中存在一些错误,这确实令人困惑。我将向 Microsoft 发送错误报告。再次感谢您的帮助!
猜你喜欢
  • 2022-12-12
  • 2014-12-09
  • 2021-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-21
  • 1970-01-01
相关资源
最近更新 更多