【问题标题】:Compile time type conversion check (constexpr and user defined literals)编译时类型转换检查(constexpr 和用户定义的文字)
【发布时间】:2012-12-13 21:58:12
【问题描述】:

更新:我在下面发布了自己的答案 这里有一个更长的版本:http://scrupulousabstractions.tumblr.com/post/38460349771/c-11-type-safe-use-of-integer-user-defined-literals

问题:

我已经制作了一个简单的constexpr 用户定义文字_X,它以 unsigned long long 形式获取值(这就是数字用户定义文字的工作方式:http://en.cppreference.com/w/cpp/language/user_literal),然后我确保该值适合在一个签名长长的。

这一切都很好(太大的值会导致编译错误),但只有当我显式创建变量时,例如

constexpr auto a= 150_X;

如果我写一些典型的东西,比如

cout << 150_X << endl;;

测试不在编译时执行。

  • 如果将 constexpr 函数分配给 constexpr 变量,它们是否仅在编译时执行? (我在标准中找不到)

  • 是否可以实现我正在寻找的_X 的安全行为?

完整示例:

#include<iostream>
#include<stdexcept>

inline constexpr long long testConv(unsigned long long v) {
  return (v > 100 ) ? throw std::exception() : v; 
} // will eventually use actual limit from numeric_limits

inline constexpr long long operator "" _X(unsigned long long f) { 
  return testConv(f) ;
}

int main(){
  constexpr auto a= 5_X;
  std::cout << a << std::endl;
  std::cout << 200_X << std::endl;  // This bad literal is accepted at compile time
  constexpr auto c=250_X;           // This bad literal is not accepted at compile time
  std::cout << c << std::endl;
}

哦,供参考:我用的是gcc4.7.2。

【问题讨论】:

    标签: c++ c++11 constexpr


    【解决方案1】:

    自我回答: 我找到了一个完整的解决方案,灵感来自我的问题的 cmets 和其他答案,以及其他问题,例如 https://stackoverflow.com/a/13384317/1149664

    解决方法是使用自定义字面量的模板形式,手动求和,将已经解析的数字乘以10。

    我在这里写了这个自我回答的详细版本:http://scrupulousabstractions.tumblr.com/post/38460349771/c-11-type-safe-use-of-integer-user-defined-literals

    template<char... Chars>
    int operator"" _steps(){
      return {litparser<0,Chars...>::value};
    }
    

    Litparser 是一个小模板元程序,它将字符列表作为参数,从 Chars 参数包保存的输入字符扩展而来。

    typedef unsigned long long ULL;
    
    // Delcare the litparser 
    template<ULL Sum, char... Chars> struct litparser;
    
    // Specialize on the case where there's at least one character left:
    template<ULL Sum, char Head, char... Rest>
    struct litparser<Sum, Head, Rest...> {
    // parse a digit. recurse with new sum and ramaining digits
      static const ULL value = litparser<
        (Head <'0' || Head >'9') ? throw std::exception() : 
        Sum*10 + Head-'0' , Rest...>::value;
    };
    
    // When 'Rest' finally is empty, we reach this terminating case
    template<ULL Sum>  struct litparser<Sum> {
      static const ULL value = Sum;
    };
    

    【讨论】:

    • 很好的答案,也感谢您的扩展写作!很有帮助。
    • 当然,这仅在文字不是八进制或十六进制时才有效。在这些情况下,将需要不同的文字解析器。
    【解决方案2】:

    constexpr 函数可能在编译时执行;也就是说,它们有资格在常量表达式中使用。如果它们没有在常量表达式中使用,那么在编译时执行它们是没有意义的,尽管我认为这是允许的。

    由于不允许将参数声明为 constexpr(第 7.1.5/1 节)[1],我认为没有任何方法可以在编译时强制评估 operator "" _X(unsigned long long),但是你也许可以用template&lt;char...&gt; operator "" _X()来做到这一点

    如果在常量表达式中调用constexpr 函数,则参数将是常量表达式(如果不是,则调用不是常量表达式)。但是,您不能通过将参数声明为constexpr强制将调用设为常量表达式,因为不允许将参数声明为constexpr,见参考标准。


    [注 1]:感谢 @LightnessRacesInOrbit 搜索标准以证明第二段中的主张。

    【讨论】:

    • Since you're not allowed to declare a parameter as constexpr 嗯,真的吗?
    • 嗯是的,真的 (7.1.5/1) - 但是 constexpr 要求是从被调用的函数“继承”的,当它作为常量表达式的一部分被调用时
    • @LightnessRacesinOrbit:如果函数作为常量表达式的一部分被调用,它只是“继承”的。您不能使用constexpr 参数编写函数,以坚持它是常量表达式的一部分。不清楚吗?
    • @LightnessRacesinOrbit:当我写下答案时 :)
    • 我不相信您在回答中涵盖了我的 cmets 中描述的范围内的主题,尽管现在我已经验证了您第二段开头的断言,答案是有道理的。
    【解决方案3】:

    Consexpr 函数不需要在编译时执行。但是你的目标是可以实现的。为了更好地理解问题,以及如何创建始终在编译时评估的迭代的示例,我推荐this post

    【讨论】:

    • 谢谢,当你写这篇文章时,我已经发布了我的答案。模板和手动解析是一种方式......为你的好博客+1(我已经关注了)。
    猜你喜欢
    • 2018-05-12
    • 2013-07-22
    • 2021-05-23
    • 1970-01-01
    • 2013-11-27
    • 2011-04-02
    • 1970-01-01
    • 2021-05-23
    • 2020-02-07
    相关资源
    最近更新 更多