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

Che*_*Alf 16 c++ templates c++11

注意:这是一个问题的答案,以便记录其他人可能觉得有用的技术,以便可能意识到其他人更好的解决方案.请随意添加批评或问题作为评论.也可以随意添加其他答案.:)


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

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

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(); }
Run Code Online (Sandbox Code Playgroud)

看起来 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(); }
Run Code Online (Sandbox Code Playgroud)

除了If_or CPPX_IF_和它的表达,它更简洁和可读.

我是怎么做到的?

Ker*_* SB 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;
Run Code Online (Sandbox Code Playgroud)

用法:

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

_t事实上,表单的"类型"别名是作为C++ 14标准库的一部分计划的,参见[meta.type.synop].


Che*_*Alf 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
Run Code Online (Sandbox Code Playgroud)

支持更受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
Run Code Online (Sandbox Code Playgroud)

这基本上只提供了一个更易读的名称,并省去::value了条件.为了还免去typename::type我使用的宏.但由于表达式通常是模板表达式,因此预处理器可能会将逗号解释为参数分隔符,以便预处理器可以看到多个参数.

我使用的解决方案(C++ 03的时间结束了)是使用C99/C++ 11 可变参数宏,...

#define CPPX_IF_( ... ) \
    typename cppx::If_T_< __VA_ARGS__ >::T
Run Code Online (Sandbox Code Playgroud)

可以定义相应的宏以使用此功能而无需使用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
Run Code Online (Sandbox Code Playgroud)

此外,为了完整性,Is_a_简单定义为......

template< class Base, class Derived_or_eq >
using Is_a_ = std::is_base_of<Base, Derived_or_eq>;
Run Code Online (Sandbox Code Playgroud)

这是usingVisual C++ 12.0确实理解的用法.


为了能够在不写入任何地方的情况下使用复合条件::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
Run Code Online (Sandbox Code Playgroud)

免责声明:没有任何代码经过广泛测试,模板元编程领域的C++编译器怪癖很常见.

  • 我相信像`std :: enable_if_t`这样的新模板别名是C++ 14的一部分. (2认同)

Cas*_*sey 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(); }
Run Code Online (Sandbox Code Playgroud)

或者更简洁:

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(); }
Run Code Online (Sandbox Code Playgroud)

我强烈建议浏览一下Concepts Lite论文的教程(第2部分),以了解在我们离开我们的enable_if领主之后世界会变得多好.

  • Concepts Lite还具有闪亮和新颖的优势,因此Microsoft可能会在他们解决C++ 98错误之前在其编译器中实现它. (2认同)