使用`std::enable_if`时如何避免编写`::value`和`::type`? [cppx]

问题描述 投票:0回答:3

注意:这是一个有答案的问题,目的是记录其他人可能认为有用的技术,并可能了解其他人更好的解决方案。请随意添加批评或问题作为评论。也请随意添加其他答案。 :)


在我的一些代码中,即标题
rfc/cppx/text/String.h
,我发现了以下神秘的片段:

template< class S, class enable = CPPX_IF_( Is_a_< String, S > ) >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

operator!
支持隐式转换为原始指针的
String
类。因此,我为此类和派生类重载(除其他外)
operator!
,以便无意中使用不支持的运算符将给出适当的编译错误,指出它毫无意义且无法访问。我认为这比默默接受这种用法并产生意外的错误结果要好得多。

CPPX_IF_
宏支持 Visual C++ 12.0 (2013) 及更早版本,这发现 C++11
using
大部分超出了它的范围。对于更符合标准的编译器,我会编写......

template< class S, class enable = If_< Is_a_< String, S > > >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

这看起来

std::enable_if

template< class S, class enabled = typename std::enable_if< Is_a_< String, S >::value, void >::type >
void operator!  ( S const& )
{ string_detail::Meaningless::operation(); }

除了

If_
CPPX_IF_
及其表达方式,更加简洁易读

我到底是怎么做到的?

c++ c++11 templates sfinae enable-if
3个回答
28
投票

在 C++14 中,变量模板使类型特征看起来更舒服。将其与 C++11 模板别名结合起来,所有的麻烦都会消失:

template <typename A, typename B>
bool is_base_of_v = std::is_base_of<A, B>::value;

template <bool B, typename T = void>
using enable_if_t = typename std::enable_if<B, T>::type;

用途:

template <typename B, typename D>
enable_if_t<is_base_of_v<B, D>, Foo> some_function(B & b, D & d) { /* ... */ }

_t
形式的“Type”别名实际上已计划作为 C++14 标准库的一部分,请参阅 [meta.type.synop]。


9
投票

熟悉 C++11-

using
编译器的设施很简单……

namespace cppx {
    using std::enable_if;

    template< class Condition_type, class Result_type = void >
    using If_ = typename enable_if<Condition_type::value, Result_type>::type;

}  // namespace cppx

对更具

using
挑战的编译器(例如 Visual C++ 12.0 及更早版本)的支持(它理解
using
的基本用法,但随着使用上下文中包含
enable_if
之类的内容越多,它变得越来越不可靠)涉及,构建在 C++03 风格的解决方案上,例如……

namespace cppx {
    using std::enable_if;

    template<
        class Condition_type,
        class Result_type = void,
        class enabled = typename enable_if<Condition_type::value, void>::type 
        >
    struct If_T_
    {
        typedef Result_type     T;
        typedef Result_type     type;
    };

}  // namespace cppx

这基本上只提供了一个更易读的名称,并省去了条件中的

::value
。为了也省去
typename
::type
我使用了宏。但由于表达式通常是模板表达式,因此预处理器可能会将逗号解释为参数分隔符,以便预处理器可能会看到多个参数。

我使用的解决方案(C++03 的时代对我来说已经结束)是使用 C99/C++11 可变宏,…

#define CPPX_IF_( ... ) \
    typename cppx::If_T_< __VA_ARGS__ >::T

可以定义相应的宏来使用此功能,而无需

typename


完整列表,归档

rfc/cppx/utility/If_.h

#pragma once
// Copyright (c) 2013 Alf P. Steinbach

#include <type_traits>      // std::enable_if

#define CPPX_IF_( ... ) \
    typename cppx::If_T_< __VA_ARGS__ >::T

namespace cppx {
    using std::enable_if;

    template< class Condition_type, class Result_type = void >
    using If_ = typename enable_if<Condition_type::value, Result_type>::type;

    template<
        class Condition_type,
        class Result_type = void,
        class enabled = typename enable_if<Condition_type::value, void>::type 
        >
    struct If_T_
    {
        typedef Result_type     T;
        typedef Result_type     type;
    };

}  // namespace cppx

此外,为了完整起见,

Is_a_
简单地定义为……

template< class Base, class Derived_or_eq >
using Is_a_ = std::is_base_of<Base, Derived_or_eq>;

这是 Visual C++ 12.0 能够理解的

using
的用法。


为了能够使用复合条件而无需到处写

::value
,以下定义会派上用场。本质上,这些是对类型进行操作的布尔运算符。也许值得注意的是,特别是一般的异或运算符,它不是以二进制 XOR 的形式实现的(例如
!=
):这会产生一个检查奇数个
true
值的运算符,这几乎没有什么意义。实用性除了恰好有两个参数的特殊情况。

namespace cppx {
    using std::integral_constant;

    template< bool c >
    using Bool_ = integral_constant<bool, c>;

    using False = Bool_<false>;     // std::false_type;
    using True  = Bool_<true>;      // std::true_type;

    template< bool v, class First, class... Rest >
    struct Count_
    {
        enum{ value = Count_<v, First>::value + Count_<v, Rest...>::value };
    };

    template< bool v, class X >
    struct Count_<v, X>
    {
        enum{ value = int(!!X::value == v) };
    };

    template< class X >
    using Not_ = Bool_<Count_<true, X>::value == 0>;                   // NOT

    template< class... Args >
    using All_ = Bool_<Count_<false, Args...>::value == 0>;            // AND

    template< class... Args >
    using Some_ = Bool_<Count_<true, Args...>::value != 0>;     // General inclusive OR.

    template< class... Args >
    using Either_ = Bool_<Count_<true, Args...>::value == 1>;   // General exclusive OR.
}  // namespace cppx

免责声明:所有代码均未经过广泛测试,并且模板元编程领域的 C++ 编译器怪癖很常见。


6
投票

我们有 C++03、C++11 和 C++14 解决方案,但缺少 Concepts Lite

template <typename Derived, typename Base>
constexpr bool Is_a_() {
  return std::is_base_of<Base, Derived>::value;
}

template<Is_a_<String> S>
void operator! ( S const& )
{ string_detail::Meaningless::operation(); }

或更简洁:

template <typename Derived, typename Base>
concept bool Is_a_() {
  return std::is_base_of<Base, Derived>::value;
}

void operator! ( Is_a_<String> const& )
{ string_detail::Meaningless::operation(); }

我强烈建议浏览一下 Concepts Lite 论文的教程(第 2 节),以了解在我们摆脱

enable_if
霸主之后,世界将会变得多么美好。

© www.soinside.com 2019 - 2024. All rights reserved.