使用"std::enable_if"时,如何避免写入"::value"和&quo
How can I avoid writing `::value` and `::type` when using `std::enable_if`? [cppx]
注意:这是一个带答案的问题,目的是记录其他人可能觉得有用的技术,并可能了解其他人;甚至更好的解决方案。请随意添加评论或问题作为评论。也可以随意添加其他答案。:)
在我的一些代码中,即标题
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++11using
在很大程度上超出了它的能力范围;
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++14中,变量模板使类型特征看起来更容易。将其与C++11模板别名结合起来,所有的cruft都消失了:
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
形式的"类型"别名计划作为C++14标准库的一部分,请参见[meta.Type.synop]。
一个舒适的带有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
之类的内容越多,它就变得越来越不可靠)有点复杂,构建在像…
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所理解的CCD_ 21的用法。
为了能够使用复合条件而不在任何地方写入::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++编译器在模板元编程领域的怪癖很常见。
我们有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
霸主之后,世界会变得多么美好。
- C++避免重复声明的语法是什么
- 在没有太多条件句的情况下,我如何避免被零除
- 如何重构类层次结构以避免菱形问题
- 函数何时会在c++中包含stack_Unwind_Resume调用
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 以下示例中如何避免代码复制?C++/库达
- Python中的for循环与C++有何不同
- 如何确保在使用基于布尔值的两个方法之一调用方法时避免分支预测错误
- 是否应该在模板化代码中完全避免const
- 我应该避免多重实现继承吗
- 为了方便起见,我应该避免公开私有字段变量吗
- 在为LINUX创建共享库时,如何避免STL的私有/弱副本
- 避免在C++中重复子类定义
- "Inverse SFINAE"避免模棱两可的过载
- 如何避免在仅标头库中C++类/变量重定义
- Python 集合.计数器,如何避免重复查找
- 是否应避免从非常量迭代器转换为常量迭代器?
- 避免矢量中的对象切片<Base><shared_ptr>
- 有没有办法在从编译器获取参数时避免预处理器宏?
- 如何避免LED在循环状态变化中闪烁?