【问题标题】:User defined literal arguments are not constexpr?用户定义的文字参数不是 constexpr?
【发布时间】:2011-12-27 20:27:00
【问题描述】:

我正在测试用户定义的文字。我想让_fac 返回数字的阶乘。

让它调用 constexpr 函数可以工作,但是它不允许我使用模板来执行此操作,因为编译器抱怨参数不是也不能是 constexpr

我对此感到困惑 - 文字不是常量表达式吗? 5_fac 中的 5 始终是可以在编译时评估的文字,那为什么我不能这样使用它呢?

第一种方法:

constexpr int factorial_function(int x) {
  return (x > 0) ? x * factorial_function(x - 1) : 1;
}

constexpr int operator "" _fac(unsigned long long x) {
  return factorial_function(x); // this works
}

第二种方法:

template <int N> struct factorial_template {
  static const unsigned int value = N * factorial_template<N - 1>::value;
};
template <> struct factorial_template<0> {
  static const unsigned int value = 1;
};

constexpr int operator "" _fac(unsigned long long x) {
  return factorial_template<x>::value; // doesn't work - x is not a constexpr
}

【问题讨论】:

  • 我认为还没有任何编译器实现这些用户定义的文字。你如何测试这个?
  • 不错!我不知道这还支持。我在某处的机器上安装了其中一个,现在我很想制定那些可变参数模板:-)

标签: c++ c++11 constexpr user-defined-literals


【解决方案1】:

我不知道 C++11 中是否有比当前接受的答案更好的方法来做到这一点,但是在 C++14 中使用轻松的constexpr,您可以编写“正常”代码:

constexpr unsigned long long int operator "" _fac(unsigned long long int x) {
    unsigned long long int result = 1;
    for (; x >= 2; --x) {
        result *= x;
    }
    return result;
}

static_assert(5_fac == 120, "!");

【讨论】:

    【解决方案2】:

    这就是我最终这样做的方式:

    template <typename t>
    constexpr t pow(t base, int exp) {
      return (exp > 0) ? base * pow(base, exp-1) : 1;
    };
    
    template <char...> struct literal;
    template <> struct literal<> {
      static const unsigned int to_int = 0;
    };
    template <char c, char ...cv> struct literal<c, cv...> {
      static const unsigned int to_int = (c - '0') * pow(10, sizeof...(cv)) + literal<cv...>::to_int;
    };
    
    template <int N> struct factorial {
      static const unsigned int value = N * factorial<N - 1>::value;
    };
    template <> struct factorial<0> {
      static const unsigned int value = 1;
    };
    
    template <char ...cv>
    constexpr unsigned int operator "" _fac()
    {
      return factorial<literal<cv...>::to_int>::value;
    }
    

    非常感谢 KerrekSB!

    【讨论】:

    • 投反对票,因为答案并没有真正回答 OP 提出的问题,而只包含代码。
    【解决方案3】:

    我可能错了,但我认为 constexpr 函数也可以使用非常量参数调用(在这种情况下,它们不会给出常量表达式,而是在运行时进行评估)。这不适用于非类型模板参数。

    【讨论】:

    • 好吧,如果你给函数传递了一个非编译时间常数,大概编译器会抱怨。这是否可以解决模板性一直渗透到代码层的问题?
    • @MichaelPrice:当然它可能会抱怨将非常量表达式传递给 constexpr 函数(正如我所说,我不确定我是否正确)。但这意味着您通常需要两个完全相同的函数,除了 constexpr 关键字,一个用于常量表达式,一个用于所有其他表达式。
    【解决方案4】:

    为了将 constexpr 与用户定义的文字一起使用,您显然必须使用可变参数模板。以wikipedia article 中的第二个清单为例。

    【讨论】:

    • 你不必总是使用可变参数模板来使用constexpr,但这种情况确实需要它(但我知道你的意思)。
    • 如何将char的可变参数模板转换为int
    • @Pubby - 减去 0x30?我不确定有什么更好的方法,但您也许可以使用部分模板专业化......虽然这个想法让我很头疼。
    • 为什么不举个例子,而不是参考维基百科?
    【解决方案5】:

    @Pubby。消化 char 非类型参数包的简单方法是将其放入字符串的初始化列表中。然后就可以使用atoi、atof等:

    #include <iostream>
    
    template<char... Chars>
      int
      operator "" _suffix()
      {
        const char str[]{Chars..., '\0'};
        return atoi(str);
      }
    
    int
    main()
    {
      std::cout << 12345_suffix << std::endl;
    }
    

    记得为 C 风格的函数添加一个空字符。

    【讨论】:

    • 问题在于它不是constexpr。这实际上比传递 unsigned long long 作为参数类型更糟糕。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 2020-12-25
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    • 2022-11-19
    • 1970-01-01
    相关资源
    最近更新 更多