如何在C ++中定义编译时三元字面量?

4
在第4版的C++编程语言书的第19章中,有一个使用模板技术定义三进制数文字的例子,但该示例无法编译。我尝试按照自己的方式修复它,但它仍无法编译。
#include <cstdint>
#include <iostream>

using namespace std;

constexpr uint64_t ipow(uint64_t x, uint64_t n)
{
  return n > 0 ? x * ipow(x, n - 1) : 1;
}

template <char c>
constexpr uint64_t base3()
{
  static_assert(c >= '0' && c <= '2', "Not a ternary digit.");
  return c - '0';
}

template <char c, char... tail>
constexpr uint64_t base3()
{
  static_assert(c >= '0' && c <= '2', "Not a ternary digit.");
  return ipow(3, sizeof...(tail)) * (c - '0') + base3<tail...>();
}

template <char... chars>
constexpr uint64_t operator""_b3()
{
  return base3<chars...>();
}

int main()
{
  cout << 1000_b3 << endl;
  return 0;
}

Clang 给出以下错误:

error: call to 'base3' is ambiguous
  return ipow(3, sizeof...(tail)) * (c - '0') + base3<tail...>();
                                                ^~~~~~~~~~~~~~
<source>:22:49: note: in instantiation of function template specialization 'base3<'0', '0'>' requested here
<source>:22:49: note: in instantiation of function template specialization 'base3<'0', '0', '0'>' requested here
<source>:28:10: note: in instantiation of function template specialization 'base3<'1', '0', '0', '0'>' requested here
  return base3<chars...>();
         ^
<source>:33:15: note: in instantiation of function template specialization 'operator""_b3<'1', '0', '0', '0'>' requested here
  cout << 1000_b3 << endl;
              ^
<source>:12:20: note: candidate function [with c = '0']
constexpr uint64_t base3()
                   ^
<source>:19:20: note: candidate function [with c = '0', tail = <>]
constexpr uint64_t base3()
                   ^
1 error generated.

什么是定义它的正确方式?
1个回答

7

目前,当tail只有一个字符(当使用自定义文字量的最后一位数字'0'时),它可能会调用base3的任何一个重载函数。

template <char c>
constexpr uint64_t base3()  // With c as '0'
template <char c, char... tail>
constexpr uint64_t base3()  // With c as '0' and tail as an empty parameter pack

没有理由偏向其中任何一个,因此它是模棱两可的。

你需要让第二个重载不使用恰好1个参数,这样你就可以确保它至少需要2个参数:

template <char c, char second, char... tail>
constexpr uint64_t base3()
{
  static_assert(c >= '0' && c <= '2', "Not a ternary digit.");
  return ipow(3, 1+sizeof...(tail)) * (c - '0') + base3<second, tail...>();
}

或者使用SFINAE实现:

template <char c, char... tail>
constexpr
typename std::enable_if<sizeof...(tail) != 0, uint64_t>::type
base3()
{
  static_assert(c >= '0' && c <= '2', "Not a ternary digit.");
  return ipow(3, sizeof...(tail)) * (c - '0') + base3<tail...>();
}

或者将其更改为0字符的基本情况:

template <typename = void>  // Can be called with an empty set of template args
constexpr uint64_t base3()
{
  return 0;
}

template <char c, char... tail>
constexpr uint64_t base3()
{
  static_assert(c >= '0' && c <= '2', "Not a ternary digit.");
  return ipow(3, sizeof...(tail)) * (c - '0') + base3<tail...>();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接