简体   繁体   English

带有继承的C ++检测习惯用法失败

[英]C++ detection idiom failure with inheritance

The below code fails to compile. 以下代码无法编译。 For some reason inheriting from HasFoo causes IsWrapper to fail. 由于某种原因,从HasFoo继承会导致IsWrapper失败。 It has something to do with the friend function foo() because inheriting from other classes seems to work fine. 它与friend函数foo()因为从其他类继承似乎可以正常工作。 I don't understand why inheriting from HasFoo causes the detection idiom to fail. 我不明白为什么从HasFoo继承会导致检测惯用法失败。

What is the proper way to detect WithFoo as a Wrapper ? WithFoo检测为Wrapper程序的正确方法是什么?

https://godbolt.org/z/VPyarN https://godbolt.org/z/VPyarN

#include <type_traits>
#include <iostream>

template<typename TagType, typename ValueType>
struct Wrapper {
  ValueType V;
};

// Define some useful metafunctions.

template<typename Tag, typename T>
T BaseTypeImpl(const Wrapper<Tag, T> &);

template<typename T>
using BaseType = decltype(BaseTypeImpl(std::declval<T>()));

template<typename Tag, typename T>
Tag TagTypeImpl(const Wrapper<Tag, T> &);

template<typename T>
using TagType = decltype(TagTypeImpl(std::declval<T>()));

// Define VoidT.  Not needed with C++17.
template<typename... Args>
using VoidT = void;

// Define IsDetected.
template<template <typename...> class Trait, class Enabler, typename... Args>
struct IsDetectedImpl
  : std::false_type {};

template<template <typename...> class Trait, typename... Args>
struct IsDetectedImpl<Trait, VoidT<Trait<Args...>>, Args...>
  : std::true_type {};

template<template<typename...> class Trait, typename... Args>
using IsDetected = typename IsDetectedImpl<Trait, void, Args...>::type;

// Define IsWrapper true if the type derives from Wrapper.

template<typename T>
using IsWrapperImpl =
  std::is_base_of<Wrapper<TagType<T>, BaseType<T>>, T>;

template<typename T>
using IsWrapper = IsDetected<IsWrapperImpl, T>;

// A mixin.

template<typename T>
struct HasFoo {
  template<typename V,
           typename std::enable_if<IsWrapper<T>::value &&
                                   IsWrapper<V>::value>::type * = nullptr>
  friend void foo(const T &This, const V &Other) {
    std::cout << typeid(This).name() << " and " << typeid(Other).name()
              << " are wrappers\n";
  }
};

template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
                 public HasFoo<WithFoo<Tag>> {};

int main(void) {
  struct Tag {};

  WithFoo<Tag> WrapperFooV;

  // Fails.  Why?
  static_assert(IsWrapper<decltype(WrapperFooV)>::value,
                "Not a wrapper");

  return 0;
}

I don't understand why inheriting from HasFoo causes the detection idiom to fail. 我不明白为什么从HasFoo继承会导致检测惯用法失败。

Isn't completely clear to me also but surely a problem is that you use IsWrapper<T> inside the body of HasFoo<T> and, when you inherit HasFoo<WithFoo<Tag>> from WithFoo<Tag> you have that WithFoo<Tag> is incomplete when you check it with IsWrapper . 对我来说还不是很清楚,但是肯定是有一个问题是,您在IsWrapper<T>的主体内使用HasFoo<T>并且当您从WithFoo<Tag>继承HasFoo<WithFoo<Tag>> ,您有了WithFoo<Tag>使用IsWrapper检查时, WithFoo<Tag>不完整。

A possible solution (I don't know if acceptable for you) is define (and SFINAE enable/disable) foo() outside HasFoo . 一个可能的解决方案(我不知道是否可以接受)是在HasFoo之外定义(和SFINAE启用/禁用) foo()

I mean... try rewriting HasFoo as follows 我的意思是...尝试如下重写HasFoo

template <typename T>
struct HasFoo {
  template <typename V>
  friend void foo(const T &This, const V &Other);
};

and defining foo() outside 并在外面定义foo()

template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
      foo(const T &This, const V &Other) {
  std::cout << typeid(This).name() << " and " << typeid(Other).name()
            << " are wrappers\n";
}

What is the proper way to detect WithFoo as a Wrapper? 将WithFoo检测为包装程序的正确方法是什么?

Sorry but your code is too complicated for me. 抱歉,您的代码对我来说太复杂了。

I propose the following (simpler, I hope) alternative 我提出以下(我希望更简单)的替代方案

#include <type_traits>
#include <iostream>

template<typename TagType, typename ValueType>
struct Wrapper {
  ValueType V;
};

template <typename T1, typename T2>
constexpr std::true_type IW_helper1 (Wrapper<T1, T2> const &);

template <typename T>
constexpr auto IW_helper2 (T t, int) -> decltype( IW_helper1(t) );

template <typename T>
constexpr std::false_type IW_helper2 (T, long);

template <typename T>
using IsWrapper = decltype(IW_helper2(std::declval<T>(), 0));

template <typename T>
struct HasFoo {
  template <typename V>
  friend void foo(const T &This, const V &Other);
};

template <typename T, typename V>
std::enable_if_t<IsWrapper<T>::value && IsWrapper<V>::value>
      foo(const T &This, const V &Other) {
  std::cout << typeid(This).name() << " and " << typeid(Other).name()
            << " are wrappers\n";
}

template<typename Tag>
struct WithFoo : public Wrapper<WithFoo<Tag>, int>,
                 public HasFoo<WithFoo<Tag>> {};

int main () {
  struct Tag {};

  WithFoo<Tag> WrapperFooV;

  static_assert(IsWrapper<decltype(WrapperFooV)>::value,
                "Not a wrapper");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM