【问题标题】:Template non-type parameter deduction模板非类型参数推导
【发布时间】:2018-05-23 03:35:02
【问题描述】:

是否可以为 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++ 不能以这种方式工作。模板参数总是在编译时推导出来的。直到运行时,您才知道函数接收哪些参数。
  • 你为什么想要那个?将值作为模板和参数传递的用例是什么?
  • 请注意“相反”是可能的:template&lt;int X&gt; int bar(const int x = X) { ... }
  • @Someprogrammerdude: "将值作为模板和参数传递的用例是什么?" 因为 C++ 没有 constexpr 参数。但非类型模板参数始终是常量表达式。因此,能够调用一个(可能是constexpr 函数,其参数可以视为constexpr 将很有用。在我们获得真正的constexpr 参数之前,您必须找到使用模板参数的解决方法。
  • @NicolBolas 我知道,但我仍然没有看到将它 both 作为模板,and 作为参数传递的意义。除了在调用函数时可能无法使用&lt;X&gt; 语法。如果常量表达式只使用模板?或者使整个函数constexpr 并传递一个常量表达式参数(如果可能)?

标签: c++ templates c++17 constexpr template-argument-deduction


【解决方案1】:

你想要的只能通过(ab)使用类型推导进行整数推导来完成。观察:

template<int x>
struct integer_value {};

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

当然,您必须使用 test(integer_value&lt;4&gt;{}) 或类似的东西来调用它。

【讨论】:

  • FWIW@op,Boost.Hana 中最简单的拼写是test(5_c)。几乎可以获得所需的准确调用语法。
  • @chris:当然。但是,据我了解,_c 的实现绝对不是微不足道的。您必须实际解析 UDL 语法的 &lt;char ...&gt; 形式。除非你有更好的解决方案?
  • 是的,很不幸。我希望这些类似 constexpr 参数的途径中的一种会成功并给我们带来更好的东西。
【解决方案2】:
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);。我可能有一些错别字,花哨的autoconstant模板可能会阻止推演,缺少0X和0B的支持。但除此之外是健全的。


基于替代循环的实现:

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.当然。另一方面,format-switch-via-overload 实在是太有趣了,但我写了一个基于循环的解析器。
猜你喜欢
  • 1970-01-01
  • 2011-10-07
  • 2018-05-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-09
  • 1970-01-01
  • 1970-01-01
  • 2022-12-22
相关资源
最近更新 更多