constexpr函数中的非constexpr调用

non-constexpr calls in constexpr functions

本文关键字:constexpr 调用 函数      更新时间:2023-10-16

这是一个简化的代码示例,用于生成任意值序列(在std::iota的意义上)和在它们之上的不同类别的迭代器:

struct delta
{
    template<typename I>
    void inc(I& i) { ++i; }
    template<typename I>
    I next(I i) { inc(i); return i; }
};

有许多类,如delta,每个定义不同的inc,如--i, i += step, i -= step, i *= step, f(i)等。函数next保持不变,实际上在基类中共享。

我们正在从inc的变异操作生成next的基于值的操作。相反的做法也是一样的,但是我们选择这种设计是为了性能,因为next只期望在一些初始化时被调用,而inc可能被调用一百万次。

问题是,如果一些参数是编译时常数,我想在编译时调用next给定constexpr参数i

这在c++ 11中是不可能的,因为调用非constexpr函数inc。只需更改为

template<typename I>
constexpr I next(I i) { inc(i); return i; }

template<typename I>
constexpr I next(I i) { return inc(i), i; }

不起作用。当然,我们可以提供另一个特殊的函数,如

template<typename I>
constexpr I next(I i) { return i + 1; }

,但这是太多的代码重复,考虑到有许多类,如delta和许多其他操作,如inc/next

我已经看到constexpr函数限制将在c++ 14中放松,但我还不能在实践中实现这一点。

:

  • 这最终会在c++ 14中工作吗?
  • 标准化的现状如何?
  • 编译器的状态是什么?
  • 有任何可能的解决方案吗?

编辑

似乎inc也应该是constexpr(虽然void)。clang 3.4:

struct delta
{
    template<typename I>
    constexpr void inc(I& i) { ++i; }
    template<typename I>
    constexpr I next(I i) { inc(i); return i; }
};

…但在GCC 4.8.2中不是这样。那么上面的代码是正确的c++ 14吗?对gcc来说,这只是时间问题吗?

这个例子不能在gcc上工作并不奇怪,根据本页,c++ 14的广义constexpr函数还不受支持。

我相信你编辑的源代码是有效的c++ 14,这里的标准草案包含,在第126页(§5.19)一个非常类似于你的例子(没有模板,非成员函数和他们使用临时):

constexpr int incr(int &n) {
    return ++n;
}
constexpr int g(int k) {
    constexpr int x = incr(k);// error: incr(k) is not a core constant
                            // expression because lifetime of k
                            // began outside the expression incr(k)
    return x;
}
constexpr int h(int k) {
    int x = incr(k);
    // OK: incr(k) is not required to be a core
    // constant expression
    return x;
}
constexpr int y = h(1); // OK: initializes y with the value 2
// h(1) is a core constant expression because
// the lifetime of k begins inside h(1)

如果我对标准的阅读是正确的,那么你的成员函数应该无关紧要,因为"this"不被求值,并且看起来代码没有违反§5.19(2)中列出的任何其他规则。