C++ 在使用CRTP的类中检测成员函数

C++ 在使用CRTP的类中检测成员函数,c++,c++14,sfinae,crtp,C++,C++14,Sfinae,Crtp,我试图根据使用CRTP的子类中可用的函数来定制基类的实现 我想要什么的基本想法: // has_inc_function<Child, void> should detect the presence of a member function void Child::inc() template<class Child, bool = has_inc_function<Child, void>::value> struct base { // ...

我试图根据使用CRTP的子类中可用的函数来定制基类的实现

我想要什么的基本想法:

// has_inc_function<Child, void> should detect the presence of a member function void Child::inc()
template<class Child, bool = has_inc_function<Child, void>::value>
struct base
{
    // ... base implementation stuff
};

template<class Child>
struct base<Child, true>
{
    // ... base specialization implementation stuff
};

struct empty : public base<empty>
{};

struct has_inc
{
    void inc()
    {}
};

struct has_inc_and_crtp : public base<has_inc_and_crtp>
{
    void inc()
    {}
};

struct has_inc_and_misuse_crtp : public base<has_inc_and_misuse_crtp, true>
{
    void inc()
    {}
};

struct has_inc_and_misuse_crtp2 : public base<has_inc_and_misuse_crtp, false>
{
    void inc()
    {}
};

struct no_inc_and_misuse_crtp : public base<no_inc_and_misuse_crtp, true>
{
};

int main()
{
    static_assert(has_inc_function<empty, void>::value == false, "");
    static_assert(has_inc_function<has_inc, void>::value == true, "");
    static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
    static_assert(has_inc_function<has_inc_and_misuse_crtp, void>::value == true, "");
    static_assert(has_inc_function<has_inc_and_misuse_crtp2, void>::value == true, "");
    static_assert(has_inc_function<no_inc_and_misuse_crtp, void>::value == false, "");
}
():

注意:这是因为实现与返回类型不匹配。为了在C++14中实现这一点,我还包括了Library fundamentals TS v2中的示例实现

struct nonesuch
{
  ~nonesuch() = delete;
  nonesuch(nonesuch const&) = delete;
  void operator=(nonesuch const&) = delete;
};

namespace detail {
template <class Default, class AlwaysVoid,
          template<class...> class Op, class... Args>
struct detector {
  using value_t = std::false_type;
  using type = Default;
};

template <class Default, template<class...> class Op, class... Args>
struct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {
  using value_t = std::true_type;
  using type = Op<Args...>;
};

} // namespace detail

template <template<class...> class Op, class... Args>
using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;

template <template<class...> class Op, class... Args>
using detected_t = typename detail::detector<nonesuch, void, Op, Args...>::type;

template <class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

template<class...> struct disjunction : std::false_type { };
template<class B1> struct disjunction<B1> : B1 { };
template<class B1, class... Bn>
struct disjunction<B1, Bn...> 
    : std::conditional_t<bool(B1::value), B1, disjunction<Bn...>>  { };

template <typename T>
using has_type_t = typename T::inc;

template <typename T>
using has_non_type_t = decltype(&T::inc);

template <typename T, class RetType>
using has_inc_function =
  disjunction<is_detected<has_type_t, T>, is_detected<has_non_type_t, T>>;
struct非这样
{
~nonesoch()=删除;
非等(非等常数&)=删除;
void运算符=(非等常数&)=删除;
};
名称空间详细信息{
模板
结构检测器{
使用value\u t=std::false\u类型;
使用类型=默认值;
};
模板

struct detector您想要的是这种形式,这显然是不可能的。在类完成之前,必须知道类的父类,因此在知道类是否有这样的成员函数之前

你能做的有点取决于base的不同实例化的不同程度。如果它们基本上是相同的接口,具有不同的实现细节,你可以编写另一个具有相同接口和变量成员的类(
std::variant
是C++17,但你可以用动态多态性做同样的事情)然后在实例化时可以决定使用哪个

您也可以尝试以下方向:

#包括
#包括
模板
结构基{
int foo();
};
struct has_inc:base{
void公司();
};
结构具有\u非\u inc:base{
};
模板
结构模拟{
int foo(base*){return 1;}
};
模板
结构模拟{
int foo(base*){return 0;}
};
模板
int base::foo(){
返回mock().foo(this);
}
int main(){
has_inc h;
没有公司;
标准::cout
我已经尝试了各种不同的实现
has_inc_函数
,但是在
has_inc_和_crtp
的情况下,它们似乎都失败了,我不知道为什么

问题(如果我理解正确)是,在
has_inc_和_crpt
情况下,首先计算
has_inc_函数的值
,以确定
子项的第二个模板参数的默认值

template<class Child, bool = has_inc_function<Child, void>::value>
struct base 
// ........................................................V  different from the default
template<class Child, bool = has_inc_function<Child, void, 1>::value>
struct base 
保持
false

我将如何实现has_inc_函数
,使其在所有这些测试用例中都能像我所期望的那样工作,或者我想要的是不可能的

一个快速而肮脏的解决方案可能是向
has_inc_函数
添加一个额外的虚拟默认模板参数

以身作则

// ................................VVVVVVV  dummy and defaulted
template <typename C, typename RT, int = 0>
struct has_inc_function
因此,当您在静态断言中使用
has_inc_functin

static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");

听起来你想要“反射”。这是C++没有的特性。J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J.J
static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
// ................................VVVVVVV  dummy and defaulted
template <typename C, typename RT, int = 0>
struct has_inc_function
// ........................................................V  different from the default
template<class Child, bool = has_inc_function<Child, void, 1>::value>
struct base 
static_assert(has_inc_function<has_inc_and_crtp, void>::value == true, "");
#include <type_traits>

template <typename C, typename RT, int = 0>
struct has_inc_function
 {
   private:
      template <typename T>
      static constexpr auto check(T *) ->
      typename std::is_same<decltype(std::declval<T>().inc()), RT>::type;

      template <typename>
      static constexpr std::false_type check(...);

      using type = decltype(check<C>(nullptr));

   public:
      /// @brief True if there is an inc member function
      static constexpr bool value = type::value;
 };

template <typename Child, bool = has_inc_function<Child, void, 1>::value>
struct base 
 { };

template <typename Child>
struct base<Child, true>
 { };

struct empty : public base<empty>
 { };

struct has_inc
 { void inc() {} };

struct has_inc_and_crtp : public base<has_inc_and_crtp>
 { void inc() {} };

struct has_inc_and_misuse_crtp : public base<has_inc_and_misuse_crtp, true>
 { void inc() {} };

struct has_inc_and_misuse_crtp2 : public base<has_inc_and_misuse_crtp, false>
 { void inc() {} };

struct no_inc_and_misuse_crtp : public base<no_inc_and_misuse_crtp, true>
 { };

template <typename C, typename RT>
constexpr auto hif_v = has_inc_function<C, RT>::value;

int main ()
 {
   static_assert(hif_v<empty, void> == false, "");
   static_assert(hif_v<has_inc, void> == true, "");
   static_assert(hif_v<has_inc_and_crtp, void> == true, "");
   static_assert(hif_v<has_inc_and_misuse_crtp, void> == true, "");
   static_assert(hif_v<has_inc_and_misuse_crtp2, void> == true, "");
   static_assert(hif_v<no_inc_and_misuse_crtp, void> == false, "");
 }