如何测试是否存在模板函数专门化

时间:2016-07-28 13:21:30

标签: c++ templates c++11 c++14 template-meta-programming

我管理单位转换。告诉我们,我达到了实现这一目标的状态。

我在不同单位之间转换的核心在于以下通用模板函数:

template <class SrcUnit, class TgtUnit> extern
double convert(double val);

此功能的目标是将以SrcUnit为单位表示的物理量值转换为以TgtUnit为单位表示的另一物理量值。

我有一个名为Quantity<Unit>的类,它管理值及其统一性,此类尝试提供类型安全和自动转换。例如,我导出以下构造函数:

  template <class SrcUnit>
  Quantity(const Quantity<SrcUnit> & q)
    : unit(UnitName::get_instance())
  {
    check_physical_units(q); // verify that the physical magnitudes are the same
    value = convert<SrcUnit, UnitName>(q.value); // <<- here the conversion is done
    check_value(); // check if the value is between the allowed interval
  }

我导出转换完成的其他内容。

因此,当有人希望管理新单元时,她会指定一个新的Unit派生类。我通过在宏中放入新类的所有必需规范来实现这一点。现在,该用户有责任编写转换函数。那就是编写convert()模板的两个特化。例如,假设您有一个名为“公里and you wish to specify a new unit called英里”的单位。在这种情况下,您可以这样做:

Declare_Unit(Mile, "mi", "English unit of length", Distance,
         0, numeric_limits<double>::max()); // macro instantiating a new Unit class
template <> double convert<Kilometer, Mile>(double val) { return val/1609.344; }
template <> double convert<Mile, Kilometer>(double val) { return 1609.344*val; }

现在,如果用户忘记编写转换函数会发生什么?那么,在这种情况下,链接器将失败,因为它无法找到convert()特化。

现在我的问题。

虽然我认为链接器错误是可接受的,因为向用户报告丢失的convert(),我想测试我是否存在convert()特化的存在时间。所以我的问题是如何实现这一目标?我想在每次调用static_assert之前通过convert()放置,测试专业化是否已知。但是怎么做呢?

PS:另外,如果有人能推荐我关于C ++元编程的好文本,对我来说非常有用。

4 个答案:

答案 0 :(得分:4)

你可以通过在主要功能模板中放置[Write] public ActionResult Create() { return View(); } 并使用一个小技巧来确保您的程序没有形成,从here无耻地窃取:

static_assert

Live Demo

答案 1 :(得分:4)

您不应向编译器承诺,when HTTP_REQUEST { log local0. "clientIP:[IP::client_addr] accessed [HTTP::host][HTTP::uri]" }的每个可能组合都存在转换:

SrcUnit/TgtUnit

相反,你应该这样做,删除通用转换器:

template <class SrcUnit, class TgtUnit>
double convert(double val);

对于您提供的每个转化 - 您应该在头文件/文件中转发声明:

template <class SrcUnit, class TgtUnit>
double convert(double val) = delete;

因此,通过这种方式,编译器将知道提供什么,不是什么

除此之外:您的原始解决方案也可能存在违反“一个定义规则”的危险。链接器无法检测到您是否错误地提供了两种不同的转换实现。

答案 2 :(得分:4)

我会用标签来解决这个问题。

template<class T>struct tag_t{constexpr tag_t(){}; using type=T;};
template<class T>constexpr tag_t<T> tag{};

这允许您将类型作为函数参数传递。

现在,我们只是调用一个函数,而不是使用专门化等。

我们将使用比convert更好的名称,例如unit_convert

namespace my_ns {
  Declare_Unit(Mile, "mi", "English unit of length", Distance,
     0, numeric_limits<double>::max()); // macro instantiating a new Unit 
  inline double unit_convert( tag_t<Meter>, tag_t<Mile>, double val ) {
    return val/1609.344
  }
  inline double unit_convert( tag_t<Mile>, tag_t<Meter>, double val ) {
    return val*1609.344
  }
}

接下来,我们写一个特征,说明unit_convert( tag<A>, tag<B>, double )是否存在:

namespace details {
  template<template<class...>class Z, class=void, class...Ts>
  struct can_apply:std::false_type{};
  template<template<class...>class Z,  class...Ts>
  struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;

template<class...Args>
using convert_unit_result = decltype( convert_unit( std::declval<Args>()... ) );

template<class SourceUnit, class DestUnit>
using can_convert_units = can_apply< convert_unit_result, tag_t<SourceUnit>, tag_t<DestUnit>, double >;

现在can_convert_units<Mile,Meter> std :: true_type ,而can_conver_units<Chicken, Egg> std :: false_type (假设鸡肉和鸡蛋属于不可转换单位,自然而然。)

作为奖励,这种技术允许您将单位放在各个名称空间中,并在那里定义它们的转换函数。如果MileMeter都尝试定义它们之间的转换,那么当您尝试转换(不明确)时,您将收到错误。转换函数可以在 命名空间中,而不是两者。

答案 3 :(得分:0)

也许这会有所帮助。

template<typename Src, typename Tgt, typename Exists = void>
double convert (double val) {
    static_assert(not std::is_void<Exists>::value, " err msg");
}

template <> double convert<Kilometer, Mile>(double val) { return val/1609.344; }