非类型模板参数推导

8

有没有可能为C++17函数推导出模板值(而不是类型)?

函数foo:

template<int I>
int foo()
{
    return (I);
}

可以通过以下方式调用:

foo<5>();

并且将返回5。

模板类型可以通过函数参数的类型进行推导。是否有可能以某种方式对模板值执行相同的操作?例如:

template<int I = x>
int bar(const int x)
{
    return (I);
}

显然这样做行不通(因为在声明之前需要一个x),但是可能会有一些C++17的技巧可以实现吗?

我想把它作为设置常量表达式函数参数的一种方式。


不行。C++ 不是这样工作的。模板参数总是在编译时推导出来的。你直到运行时才知道一个函数接收什么参数。 - Sam Varshavchik
你为什么想要那个?将值作为模板和参数传递的用例是什么? - Some programmer dude
2
请注意,“相反”的情况也是可能的:template<int X> int bar(const int x = X) { ... } - Some programmer dude
@Someprogrammerdude: "将值作为模板和参数传递的用例是什么?" 因为C++没有constexpr参数。但是,非类型模板参数始终是常量表达式。因此,能够调用(可能是constexpr函数的参数,它可以视为constexpr)将是有用的。在我们获得真正的constexpr参数之前,您必须使用模板参数找到解决方法。 - Nicol Bolas
@NicolBolas 我知道,但我仍然不明白既作为模板又作为参数传递的意义所在。除了可能在调用函数时无需使用<X>语法之外。如果是常量表达式,只使用模板?或者将整个函数设为constexpr并传递一个常量表达式参数(如果可能的话)? - Some programmer dude
2个回答

6
你所需要的只能通过滥用整数类型推导来实现。请看下面的示例:
template<int x>
struct integer_value {};

template<int x>
void test(integer_value<x> val)
{
  //x can be used here.
}

当然,你必须使用test(integer_value<4>{})或类似的方式来调用它。

2
对于@op,Boost.Hana中最简单的写法是test(5_c)。几乎可以获得所需的完美函数调用语法。 - chris
@chris:当然。但是据我所知,_c的实现显然并不简单。您必须实际解析UDL语法的<char ...>形式。除非您有更好的解决方案? - Nicol Bolas
是的,这很不幸。我希望其中一种类似于constexpr参数的途径能够实现并为我们带来更好的东西。 - chris

3
template<auto x>
using constant = std::integral_constant< std::decay_t<decltype(x)>, x >;

模板
using constant = std::integral_constant< std::decay_t<decltype(x)>, x >;
constexpr int digit_val( char c ) {
    if (c >= '0' && c <='9')
        return c-'0';
    else if (c >= 'A' && c <= 'Z')
        return c-'A'+10;
    else if (c >= 'a' && c <= 'z')
        return c-'a'+10;
    else
        throw nullptr;
}
constexpr long long ce_pow( int base, int count, long long acc=1 ){
  if (!count) return acc;
  return ce_pow( base, count-1, acc*(long long)base );
}
constexpr long long from_digits( long long acc, int base ){
  return acc;
}
template<int I, int...Is>
constexpr long long from_digits( long long acc, int base, constant<I>, constant<Is>... ) {
  return from_digits( acc+ce_pow(base, sizeof...(Is))*(long long)I, base, constant<Is>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<'x'>, constant<cs>... ){
  return from_digits( 0, 16, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<'b'>, constant<cs>... ){
  return from_digits( 0, 2, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr long long val( constant<'0'>, constant<cs>... ){
  return from_digits( 0, 8, constant<digit_val(cs)>{}... );
}
template<char...cs>
constexpr auto operator""_k(){
    return constant< val( constant<cs>{}... ) >{};
}

或类似的。现在:
template<int I>
int bar(constant<I>)
{
  return (I);
}

应该与 bar(5_k); 一起使用。我可能会有一些打字错误,而花哨的 auto constant 模板可能会阻止推导,缺少对 0X0B 的支持。但除此之外就没问题了。


基于循环的替代实现:

struct number_format {
    int prefix = 0;
    int base = 0;
};
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'x'>, constant<cs>... ) {
    return {2,16};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'X'>, constant<cs>... ) {
    return {2,16};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'b'>, constant<cs>... ) {
    return {2,2};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<'B'>, constant<cs>... ) {
    return {2,2};
}
template<char...cs>
constexpr number_format get_format( constant<'0'>, constant<cs>... ) {
    return {1,8};
}
template<char...cs>
constexpr number_format get_format( constant<cs>... ) {
    return {0,10};
}
template<char...Cs>
constexpr long long val( constant<Cs>...cs ){
  char buff[] = {Cs...};
  constexpr number_format fmt = get_format( cs... );

  long long r = 0;
  for (auto it = std::begin(buff)+fmt.prefix; it != std::end(buff); ++it) {
      r *= fmt.base;
      r += digit_val(*it);
  }
  return r;
}
template<char...cs>
constexpr auto operator""_k(){
    return constant< val( constant<cs>{}... ) >{};
}

live examples.


你可以立即将字符存储到数组中,并正常解析它。 - T.C.
@T.C. 当然。另一方面,通过重载进行格式切换太有趣了,但我编写了一个基于循环的解析器。 - Yakk - Adam Nevraumont

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