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

问题描述 投票:0回答:3

考虑以下代码:

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)
}
c++ c++11 constexpr constexpr-function
3个回答
29
投票

constexpr
函数和
constexpr
变量是相关的,但又是不同的东西。

constexpr
变量是保证其值在编译时可用的变量。

A

constexpr
函数是一个函数,如果使用
constexpr
参数求值,则 and 在执行期间表现“正确”,将在编译时求值。

如果将非

constexpr
int
传递给
constexpr
函数,它不会神奇地使其在编译时进行评估。但是,它将被允许通过自身传递其输入参数的
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
投票
const

constexpr
之间的一个重要区别是
constexpr
可以在编译时评估。

通过编写

constexpr int ii = make_const(i);

,您可以告诉编译器该表达式将在编译时求值。由于

i
是在运行时评估的,因此编译器无法执行此操作并给出错误。
    


2
投票
constexpr

函数。 Constexpr 期望参数在编译时已知。

© www.soinside.com 2019 - 2024. All rights reserved.