【问题标题】:C++ detection idiom failure with inheritance带有继承的 C++ 检测习语失败
【发布时间】:2019-08-06 22:23:49
【问题描述】:

以下代码编译失败。由于某种原因,从 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;
}

【问题讨论】:

  • 好的,我认为这是我能做到的最低限度。

标签: c++ variadic-templates template-meta-programming sfinae template-argument-deduction


【解决方案1】:

我不明白为什么从HasFoo 继承会导致检测习语失败。

我也不是很清楚,但肯定有一个问题是你在HasFoo&lt;T&gt; 的主体内使用IsWrapper&lt;T&gt;,并且当你从WithFoo&lt;Tag&gt; 继承HasFoo&lt;WithFoo&lt;Tag&gt;&gt; 时,你会发现WithFoo&lt;Tag&gt; 是不完整的你用IsWrapper检查一下。

一个可能的解决方案(我不知道你是否可以接受)是定义(和 SFINAE 启用/禁用)foo() 外部 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";
}

检测 WithFoo 作为 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");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多