注意:这是一个有答案的问题,目的是记录其他人可能认为有用的技术,并可能了解其他人更好的解决方案。请随意添加批评或问题作为评论。也请随意添加其他答案。 :)
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++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]。
熟悉 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++ 编译器怪癖很常见。
我们有 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
霸主之后,世界将会变得多么美好。