【问题标题】:C++17 how to test class has a member variable? [duplicate]C++17如何测试类有成员变量? [复制]
【发布时间】:2021-05-10 17:00:53
【问题描述】:

我要走这条路

struct S {
  static constexpr int extra=5;
};

struct V {
};


template <typename T>
void f()
{  
  if (std::is_same_v<decltype(T::extra), int>)
    std::cout<< "extra exists" <<std::endl;
}

但调用 f&lt;S&gt;() 失败

std::is_same_v<decltype(S::extra), int> == 0

f&lt;V&gt;() 无法编译

【问题讨论】:

    标签: c++ c++17 typetraits


    【解决方案1】:

    观察decltype(T::extra)(当TS时)是int const(是constexpr所以也是const),而不是int。这解释了为什么f&lt;S&gt;() 失败。

    我想测试一个类是否有成员变量有很多方法。

    一个可能的解决方案是开发一些东西

    void type_extra (...);
    
    template <typename T>
    auto type_extra (T t) -> decltype(t.extra);
    
    template <typename T>
    using type_extra_t = decltype(type_extra(std::declval<T>()));
    

    现在你可以写f()如下

    template <typename T>
    void f()
    {  
      if ( not std::is_same_v<type_extra_t<T>, void> )
       {
         std::cout<< "extra exists" <<std::endl;
    
         if ( std::is_same_v<type_extra_t<T>, int> )
            std::cout<< "(and is int)" << std::endl;
       }
    }
    

    注意现在type_extra_t&lt;S&gt;int,而不是int const;这种方式(从函数的返回类型中获取类型)松散了变量的常量。

    如果你想保持常量,你可以从函数返回一个引用(所以它在S的情况下返回一个int const &amp;

    template <typename T>
    auto type_extra (T t) -> decltype(t.extra) &;
    

    并从 using 中删除引用(因此,在 S 的情况下,int const &amp; 变为 int const

    template <typename T>
    using type_extra_t
       = std::remove_reference_t<decltype(type_extra(std::declval<T>()))>;
    

    【讨论】:

    • 有趣。有同样的想法,我尝试了std::remove_cv&lt;decltype(T::extra)&gt; 的运气,但这没有用。也许,我监督了一些事情......My try on coliru......虽然你的方法似乎工作正常:Demo on coliru
    • 不知道 C++ 有替代标记 not,使条件表达式更容易阅读
    • @Scheff - 你必须使用typename std::remove_cv&lt;...&gt;::type(或std::remove_cv_t&lt;...&gt;),而不仅仅是std::remove_cv&lt;...&gt;(你必须提取内部type
    • @M.A - 我同意:我发现很难阅读 ! condition
    • 不知怎的,我期待我监督了一些愚蠢的事情......现在,它起作用了:Demo on coliru。感谢您的启发。
    【解决方案2】:

    如果您被困在,您可以添加一些基础设施来使此类检测更容易。

    检测成语

    检测此类特征的最可重用/一致的方法是通过Detection idiom,它通过模板中的std::void_t利用SFINAE。

    这可以从 cppreference 的std::experimental::is_detected 的页面逐字获取。这有效地为 C++17 提供了以类似于 C++20 概念的方式检测特征的能力;并且该基础架构可以轻松地重复用于几乎任何检测。

    您需要的基本知识是:

    #include <type_traits>
    
    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
     
    struct nonesuch{};
    
    template <template<class...> class Op, class... Args>
    using is_detected = typename detail::detector<nonesuch, void, Op, Args...>::value_t;
    

    注意:上述基础设施可重复用于任何检测。它是 C++17 中非常有用的可复用工具。

    使用is_detected,您只需要一个检测器,它只是一个模板别名,计算结果为decltype 表达式,表示可能存在或不存在的事物。

    因此,在您的情况下,要有条件地检测 T::extra 的存在,您可以使用简单的检测器来执行此操作,例如:

    template <typename T>
    using detect_extra = decltype(T::extra);
    

    现在把它们放在一起,你可以使用这个检测器有条件地切换分支:

    if constexpr (is_detected<detect_extra,T>::value) {
        // Only do code if 'T' has 'T::extra' (e.g. 'S')
    } else {
        // Only do code if 'T' does not have 'T::extra' (e.g. 'V')
    }
    

    Live Example


    如果对特定类型的等效转换很重要,例如extra需要转换为int,您也可以使用is_detected_convertible 并使用检测器检查结果是否可以转换为所需的类型。再次使用相同的 cppreference 页面,您可以将is_detected_convertible 定义为:

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

    这允许支票改为:

    if constexpr (is_detected_convertible<int, detect_extra, T>::value) {
        // Only do code if 'T' has 'T::extra' convertible to int (e.g. 'S')
    } else {
        // Only do code if 'T' does not have 'T::extra', or is not int
    }
    

    Live Example

    概念(仅限 C++20+)

    如果您可以访问 及其他权限,concepts 会更简单——因为您可以简单地使用 concept + requires 子句,例如:

    #include <concepts> // std::same_as
    
    template <typename T>
    concept HasExtra = requires(T) {
      {T::extra} -> std::same_as<int>;
    };
    
    if constexpr (HasExtra<T>) {
        // Only do code if 'T' has 'T::extra' and is 'int' (e.g. 'S')
    } else {
        // Only do code if 'T' does not have 'T::extra' (e.g. 'V')
    }
    

    Live Example

    【讨论】:

      猜你喜欢
      • 2019-10-15
      • 2020-01-29
      • 2021-04-04
      • 1970-01-01
      • 1970-01-01
      • 2015-05-04
      • 1970-01-01
      • 2015-10-25
      • 1970-01-01
      相关资源
      最近更新 更多