C++ Programming Language中的narrow_cast实现

在C++中,各种数值类型的转化是C++编译过程中警告的主要来源,但是,很多时候,我们需要使用各种数值类型,例如我们用数组的某一位表示大小为对应序号的值,这种情况下,经常会涉及多种数值类型。根据C++ Programming Language中的建议,在数值类型转换时,使用narrow_cast来实现运行时安全,这里给出C++14版本的实现。

// there is no implicit conversion from Source to Target
template <typename Target, typename Source,
    typename = std::enable_if_t<
    !std::is_same<std::common_type_t<Target, Source>, std::decay_t<Target>>::value>>
    inline Target narrow_cast(Source v)
{
    static_assert(!std::is_reference<Target>::value, "The target couldn't be reference");
    static_assert(std::is_arithmetic<Source>::value, "The parameter of narrow_cast should be arithmetic");
    static_assert(std::is_arithmetic<Target>::value, "The return value of narrow_cast should be arithmetic");

    // using Target_U = std::remove_reference_t<Target>;
    // using Source_U = std::remove_reference_t<Source>;

    auto r = static_cast<Target>(v);
    if (static_cast<Source>(r) != v)
        throw std::runtime_error("narrow_cast<>() failed");
    return r;
}

// there is implicit conversion from Source to Target
template <typename Target, typename Source,
    typename = std::enable_if_t<
    std::is_same<std::common_type_t<Target, Source>, std::decay_t<Target>>::value>>
    inline constexpr std::remove_reference_t<Source> narrow_cast(Source v)
{
    static_assert(!std::is_reference<Target>::value, "The target couldn't be reference");
    static_assert(std::is_arithmetic<Source>::value, "The parameter of narrow_cast should be arithmetic");
    static_assert(std::is_arithmetic<Target>::value, "The return value of narrow_cast should be arithmetic");

    return static_cast<Target>(v);
}

下面给出,使用Catch写的简单测试用例:

#define CATCH_CONFIG_MAIN
#include "catch.hpp"
#include <cmath>

TEST_CASE("Test narrow_cast", "[narrow_cast]")
{
    int i = 10;
    long long j = 15;
    long long& k = j;
    REQUIRE(narrow_cast<long>(k) == 15);
    REQUIRE(narrow_cast<long>(i) == 10);
    long long very_big = pow(10, 12);
    bool exception = false;
    try
    {
        narrow_cast<long>(very_big) == very_big;
    }
    catch (const std::runtime_error& error)
    {
        exception = true;
    }
    REQUIRE(exception);
    //REQUIRE(narrow_cast<long&>(k) == 15);
    //REQUIRE(narrow_cast<long&>(i) == 10);
}

测试可知,在转化的类型可以容纳时,narrow_cast可以正常运行,如果narrow_cast转化后的值与原值不同时,会抛出runtime_error的异常。