【问题标题】:Checking at compile time if specified value is in a range of a type在编译时检查指定的值是否在类型的范围内
【发布时间】:2011-04-05 18:14:06
【问题描述】:

是否可以检查:

template<class IntType,IntType value>
struct X{};

我的意思是,是否可以检查用户提供的值是否“适合”IntType(可以是任何标准整数类型)类型?例如,我想检测这样的东西:

X<char,300> a;//here 300 is out of range and I would like to be able to detect that.

【问题讨论】:

  • @GMan:他的目标是在编译时检查给定的模板参数是否在给定数值类型的允许范围内。
  • @There @Tomalak:对,为什么What's the bigger picture?
  • @There @Tomalak: So we can solve problems rather than answer questions. 如果将其用于某些实际目的并且我们知道该目标,我们可以采取完全不同且可能更合适的路线。询问步骤永远不如询问目标有用。如果有人问“如何调整动态数组的大小?”答案是使用std::vector,而不是继续沿着错误的路径前进。你可以这样回答,因为你知道目标,而不是步骤。
  • @GMan:我最不同意 Stack Overflow 上的“回答问题,而不是问题”。这是一个问答网站,而不是一个 P&A 网站。如果有人提出问题,请回答...不要再猜测他们,并尝试仔细检查他们这样做的动机。 (无论如何,这将在 cmets 中自然而然地出现。:P)
  • @Tomalak:我不同意直接的问答方式。如果提问者每次都知道他们想要做什么,那很好,但大多数时候(也就是说,并非例外),他们不知道。我的目标是帮助人们,这意味着了解他们的目标。把我的头埋在沙子里,表现得好像我没有看到真正的问题或更好的方法只会伤害人们。

标签: c++ metaprogramming range-checking


【解决方案1】:

现在您已将 X 的签名与原始未编辑问题中的签名方式进行了更改,使用 Boost.Integer 即可轻松实现:

#include <boost/static_assert.hpp>
#include <boost/cstdint.hpp>
#include <boost/integer_traits.hpp>

template<
    typename IntType,
    boost::uint64_t Value,
    bool IsSigned = boost::integer_traits<IntType>::is_signed
>
struct validate_range;

template<typename IntType, boost::uint64_t Value>
struct validate_range<IntType, Value, true>
{
    typedef boost::integer_traits<IntType> traits_t;
    static bool const value =
        static_cast<boost::int64_t>(Value) >= traits_t::const_min &&
        static_cast<boost::int64_t>(Value) <= traits_t::const_max;
};

template<typename IntType, boost::uint64_t Value>
struct validate_range<IntType, Value, false>
{
    typedef boost::integer_traits<IntType> traits_t;
    static bool const value =
        Value >= traits_t::const_min &&
        Value <= traits_t::const_max;
};

template<typename IntType, boost::uint64_t Value>
struct X
{
    BOOST_STATIC_ASSERT_MSG(
        (validate_range<IntType, Value>::value),
        "Value constant is out of range"
    );
};

int main()
{
    X<char, -2> x1;             // fails iif char is unsigned by default
    X<char, 2> x2;              // fine
    X<char, 255> x3;            // fails iif char is signed by default
    X<unsigned char, -2> x4;    // fails
    X<unsigned char, 255> x5;   // fine
    X<unsigned char, 300> x6;   // fails
}

【讨论】:

  • 问题是unsigned转换成signed的结果是实现定义的,所以我说没有通用的解决方案。但该方法肯定是 +1。
  • @GMan :重要的是,将有符号转换为无符号到有符号的往返可以正确地返回值,我相信在所有主流编译器中都可以安全地假设。
  • 是的,因此 +1,但这不是一个通用的解决方案。
【解决方案2】:

Boost 是正确的方法,但您真正想要的是即将到来的新 C++0x 标准:静态断言。 Boost 已经在 boost_staticassert 中实现了它。

【讨论】:

  • 这并不容易,但应该这样做(警告,未检查):BOOST_STATIC_ASSERT(std::numeric_limits::max() > 300); (在这里你可以使用 boost::integer_traits 代替 numeric_limit 。
  • std::numeric_limits&lt;T&gt;::{max(),min()} 不是恒定的。祝你在编译时使用它们好运。
  • 糟糕,没有看到您的答案。 +1,这是我刚刚输入的内容。
  • @Tomalak : FWIW boost::integer_traits&lt;T&gt;::{const_max,const_min} 常量表达式。
【解决方案3】:

没有。给定您的代码,300 会在您看到之前由编译器转换为 char

您可以做的最接近的事情是将参数接受为一个整数参数,该参数的范围大于您的目标类型。然后在转换之前检查该值是否合适。唯一的问题是signedunsigned,我认为没有通用的解决方案。

但不用担心:确保正确提供参数不是您班级的工作;那将是一个根本不存在的实用程序类型的工作。无论好坏,C++ 都没有为此提供干净的机制,因为它假定程序员不会犯这些错误。

【讨论】:

  • 同意。只希望程序员不要。如果他们这样做了,那是他们自己的错。
【解决方案4】:

我会说这个问题的直接解决方案可能是这样的:

   template< typename T, T X, T L, T H>
      using inside_t = 
        std::enable_if_t< (X <= H) && (X >= L), 
           std::integral_constant<T, X> >;

应用于 OP:

    template<typename C, unsigned K>    struct X; // final {};

template<unsigned K>
struct X<char, K> final 
{
    using ascii_ordinal = inside_t<unsigned, K, 0, 127>;
    char value = char(ascii_ordinal::value);
};

当它完成这项工作时会呈现非常糟糕的 CL 错误消息:

X<char, 300> a; //here 300 is out of range and I would like to be able to detect that.

虽然不那么时髦但最舒适的 API 可能是:

template<unsigned K>
struct X<char, K> final 
{
    static_assert( K >= 0U && K <= 127U, "\n\nTeribly sorry, but value must be between 0 and 127 inclusive\n\n") ;
    char value = char(K);
};

【讨论】:

    【解决方案5】:

    在 C++20 中使用 std::in_range

    if constexpr (std::in_range<char>(300)) {
        // ...
    }
    

    还有intcmp自己做对比

    int val = 300;
    if constexpr (std::cmp_greater(val, std::numeric_limits<char>::max()))
    {
        std::cout << "Overflow\n";
    }
    else if constexpr (std::cmp_less(val, std::numeric_limits<char>::min()))
    {
        std::cout << "Underflow\n";
    }
    else
    {
        std::cout << "In range\n";
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      • 1970-01-01
      • 2016-05-03
      • 1970-01-01
      相关资源
      最近更新 更多