我管理单位转换。告诉我们,我达到了实现这一目标的状态。
我在不同单位之间转换的核心在于以下通用模板函数:
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()
放置,测试专业化是否已知。但是怎么做呢?
答案 0 :(得分:4)
你可以通过在主要功能模板中放置[Write]
public ActionResult Create()
{
return View();
}
并使用一个小技巧来确保您的程序没有形成,从here无耻地窃取:
static_assert
答案 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 (假设鸡肉和鸡蛋属于不可转换单位,自然而然。)
作为奖励,这种技术允许您将单位放在各个名称空间中,并在那里定义它们的转换函数。如果Mile
和Meter
都尝试定义它们之间的转换,那么当您尝试转换(不明确)时,您将收到错误。转换函数可以在 命名空间中,而不是两者。
答案 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; }