在常量表达式中不能使用constexpr函数的函数参数。

22
考虑以下代码:
static constexpr int make_const(const int i){
    return i;
}

void t1(const int i)
{
    constexpr int ii = make_const(i);  // error occurs here (i is not a constant expression)
    std::cout<<ii;
}

int main()
{
   t1(12);
}

为什么在 make_const 调用时出现错误?

更新

但这个有效:

constexpr int t1(const int i)
{
    return make_const(i);
}

然而,这不是:
template<int i>
constexpr bool do_something(){
    return i;
}

constexpr int t1(const int i)
{
    return do_something<make_const(i)>();   // error occurs here (i is not a constant expression)
}

1
因为在一般情况下,在void t1(const int)中,i不是constexpr - stefan
1
我该如何使它成为constexpr呢? - tower120
我想将“12”转发到“do_something”模板参数。 - tower120
6
没有直接的方法来做你想做的事情。这个特性/限制可能是关于 constexpr 最经常被问到的问题。 - Marc Glisse
3
可能是[为什么将constexpr函数的两个参数进行比较不是静态断言的常量条件?]的重复问题。 - Aconcagua
显示剩余6条评论
3个回答

30
一个`constexpr`函数和一个`constexpr`变量是相关但不同的东西。
`constexpr`变量是一种变量,其值在编译时保证可用。
`constexpr`函数是一种函数,如果使用`constexpr`参数进行评估并且在执行期间表现“正确”,则将在编译时评估该函数。
如果向`constexpr`函数传递非`constexpr`的`int`,它不会自动使其在编译时评估。但是,它将允许通过其输入参数的`constexpr`特性(正常函数无法做到这一点)。
函数上的`constexpr`是文档和限制如何编写以及对编译器的指令的混合。
其背后的原因是允许相同的函数在编译时和运行时都被评估。如果传递了运行时参数,则它是运行时函数。如果传递了`constexpr`参数,则可以在编译时评估它(如果在某些上下文中使用,则会评估)。
请注意,`consteval`可能是您要寻找的函数。但也可能不是。
您正在收到错误,因为通过传入运行时值,您无法获得编译时值。
有解决方法。我最喜欢的是`std::variant`的`std::integer_constant`;您可以在运行时选择哪个处于活动状态,然后使用`std::visit`获取编译时常量。缺点是这很容易生成大量的代码。
template<auto I>
using constant_t=std::integral_constant<decltype(I),I>;
template<auto I>
constexpr constant_t<I> constant_v={};
template<auto...Is>
using var_enum_t=std::variant<constant_t<Is>...>;
template<class Indexes>
struct var_enum_over;
template<class Indexes>
using var_enum_over_t=typename var_enum_over<Indexes>::type;
template<class T,T...ts>
struct var_enum_over<std::integral_sequence<T,Is...>>{
  using type=var_enum_t<Is...>;
};
template<std::size_t N>
using var_index_t=var_enum_over_t<std::make_index_sequence<N>>;

template<std::size_t N>
var_index_t<N> var_index(std::size_t I){
  constexpr auto table=[]<std::size_t...Is>(std::index_sequence<Is...>)->std::array<N,var_index_t<N>>{
    return { var_index_t<N>(constant_v<Is>)..., };
  }(std::make_index_sequence<N>{});
  if (I>=N) throw 0; // todo: something better
  return table[I];
}

现在您可以:

auto idx=var_index<5>(3/* 3 can be runtime */);
std::visit([](auto three){
  // three is a compile time value here
}, idx);

11

constconstexpr之间的一个重要区别是,constexpr可以在编译时计算。

通过编写 constexpr int ii = make_const(i);,您告诉编译器该表达式将在编译时计算。由于i在运行时计算,编译器无法这样做并给出错误。


1

由于t1()不是constexpr函数,因此参数i是运行时变量...不能传递给constexpr函数。 Constexpr期望参数在编译时已知。


1
将t1设置为constexpr不会改变任何东西。 - Marc Glisse
是的,我测试了你的建议。老调重弹。 - tower120
@MarcGlisse 的 t1() 函数形式不正确,不能成为 constexpr 函数,我并不是建议将其改为 constexpr 就能解决问题。 - oz10
@praxos1977 - 请查看更新后的问题。您的建议是错误的。 - tower120
2
@tower120,我并不是在提出建议。我只是指出你不能将运行时变量传递给constexpr函数。显然TAS的回答比我的表达更好。 - oz10

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