C++ 检测习语失败与继承

C++ detection idiom failure with inheritance

以下代码编译失败。由于某种原因,从 HasFoo 继承会导致 IsWrapper 失败。它与 friend 函数 foo() 有关,因为从其他 类 继承似乎工作正常。我不明白为什么从 HasFoo 继承会导致检测成语失败。

WithFoo 检测为 Wrapper 的正确方法是什么?

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<T> 的正文中使用 IsWrapper<T> 并且当你从 [=17= 继承 HasFoo<WithFoo<Tag>> 时] 当您用 IsWrapper.

检查时,您发现 WithFoo<Tag> 不完整

一个可能的解决方案(我不知道你是否可以接受)是 define (and SFINAE enable/disable) foo() outside HasFoo.

我的意思是...尝试重写 HasFoo 如下

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

并在

之外定义 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?

抱歉,你的代码对我来说太复杂了。

我提出以下(我希望更简单)替代方案

#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");
}