【问题标题】:Constexpr member functionconstexpr 成员函数
【发布时间】:2020-03-10 23:50:26
【问题描述】:

假设我有一个由引擎参数化的struct 模板S

template<class Engine> struct S;

我有两个引擎:一个带有constexpr 成员函数size() 的“静态”引擎,以及一个带有非constexpr 成员函数size() 的“动态”引擎:

struct Static_engine {
    static constexpr std::size_t size() {
        return 11;
    }
};

struct Dynamic_engine {
    std::size_t size() const {
        return size_;
    }
    std::size_t size_ = 22;
};

如果引擎的size()constexpr,我想在S 中定义可以用作constexprsize() 成员函数。我写:

template<class Engine>
struct S {
    constexpr std::size_t size() const {
        return engine_.size();
    }
    Engine engine_;
};

然后下面的代码用 GCC、Clang、MSVC 和 ICC 编译:

S<Static_engine> sta;         // not constexpr
S<Dynamic_engine> dyn;

constexpr auto size_sta = sta.size();
const auto size_dyn = dyn.size();

考虑到constexpr 的复杂性和各种“格式错误,不需要诊断”,我仍然有一个问题:这段代码格式正确吗?

Full code on Godbolt.org

(我用 标记了这个问题,以防此代码在这两个标准中具有不同的有效性。)

【问题讨论】:

  • "格式错误,不需要诊断" 关于constexpr 函数的内容并不多。如果你试图滥用他们的constexpr 状态,编译器必须是嘈杂的。

标签: c++17 c++20 c++ c++17 constexpr c++20


【解决方案1】:

代码写得很好。

[dcl.constexpr]

6 如果实例化模板特化了一个 constexpr 类模板的函数模板或成员函数将无法 满足 constexpr 函数或 constexpr 的要求 构造函数,该特化仍然是 constexpr 函数或 constexpr 构造函数,即使调用这样的函数不能 出现在常量表达式中。如果没有专门化的模板 将满足 constexpr 函数或 constexpr 的要求 构造函数当被视为非模板函数或构造函数时, 模板格式错误,无需诊断。

对于使用Dynamic_engine 的特化,该成员可能不会出现在常量表达式中,但正如上面段落所详述的那样,这不会使S::size 格式错误。我们也远离非良构 NDR 领域,因为有效的实例化可能的。 Static_engine 就是一个很好的例子。

引用来自最新的 C++17 标准草案 n4659,类似的措辞出现在最新的 C++20 草案中。


至于sta.size() 作为常量表达式的评估,在[expr.const] 的列表中,我找不到评估本身不允许的任何内容。因此它是一个有效的常量表达式(因为列表告诉我们什么无效)。通常,要使constexpr 函数有效,只需要存在 一些 组参数,其求值会产生一个有效的常量表达式。如以下示例形式的标准所示:

constexpr int f(bool b)
  { return b ? throw 0 : 0; }           // OK
constexpr int f() { return f(true); }   // ill-formed, no diagnostic required

struct B {
  constexpr B(int x) : i(0) { }         // x is unused
  int i;
};

int global;

struct D : B {
  constexpr D() : B(global) { }         // ill-formed, no diagnostic required
                                        // lvalue-to-rvalue conversion on non-constant global
};

【讨论】:

  • 包含此子句现在具有的示例会有所帮助:eel.is/c++draft/dcl.constexpr#6
  • @Barry - 恐怕我很困惑。这个特定的子句在编号中移动,现在是 p7。上一个子句的例子在这里也相关吗?真正的问题。
  • 它们非常相关。一个只用于常规功能,一个用于模板。一般而言,如果不存在作为常量表达式的单个公式,则仅是格式错误的 ndr。否则,没关系。
【解决方案2】:

是的。

函数可以标记为constexpr,而无需在编译时强制进行评估。只要您满足将函数标记为constexpr 的其他要求,就可以了(返回文字类型,参数是文字,没有内联汇编等)。唯一可能遇到的问题是,如果实际上不可能创建满足作为核心常量表达式调用的函数的参数。 (例如,如果您的函数对所有值都有未定义的行为,那么您的函数将是格式错误的 NDR)

在 C++20 中,我们收到了 consteval 说明符,它强制对函数的所有调用都能够生成编译时常量 (constexpr)。

【讨论】:

  • consteval 比这更强大——它表示每个 调用 都必须产生一个常量表达式(可能是间接的)。
  • @DavisHerring:说得好。为清晰起见进行了更新。
【解决方案3】:

不是直接回答,而是另一种方式:

struct Dynamic_Engine
{
  using size_type = size_t;

  size_type size() const
  {
    return _size;
  }

  size_type _size = 22;
};
struct Static_Engine
{
  using size_type = std::integral_constant<size_t, 11>;

  size_type size() const
  {
    return size_type();
  }
};

template <typename ENGINE>
struct S
{
  auto size() const
  {
    return _engine.size();
  }

  ENGINE _engine;
};

int main()
{
  S<Static_Engine>  sta;
  S<Dynamic_Engine> dyn;

  const auto size_sta = sta.size();
  const auto size_dyn = dyn.size();

  static_assert(size_sta == 11);
}

我遇到了同样的问题,恕我直言,最简单、更通用的解决方案是使用std::integral_constant。不再需要处理constexpr,因为大小信息直接编码到类型中

如果您仍然真的想使用constexpr(有额外的复杂性),您可以这样做:

struct Dynamic_Engine
{
  size_t size() const
  {
    return _size;
  }

  size_t _size = 22;
};
struct Static_Engine
{
  static constexpr size_t size() // note: static 
  {
    return 11;
  }
};

template <typename ENGINE>
struct S
{
  constexpr size_t size() const
  {
    return _engine.size();
  }

  ENGINE _engine;
};

int main()
{
  S<Static_Engine>  sta;
  S<Dynamic_Engine> dyn;

  constexpr size_t size_sta = sta.size();
  const size_t size_dyn = dyn.size();

  static_assert(size_sta == 11);
}

【讨论】:

  • “静态”选项实际上不需要实际生成单个常量,只要该对象可用于常量表达式,其size() 即可。
  • @DavisHerring 感谢您的评论。实际上,在我的代码中,我确实使用了std::integral_constant 方法。对于static 方法,我添加了inline,它必须解决这个问题。
  • @DavisHerring,你是对的,而且我误读了你最初的评论。我已经删除了这一切。抱歉打扰了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-05
  • 1970-01-01
  • 1970-01-01
  • 2022-01-03
  • 1970-01-01
  • 2016-07-29
  • 1970-01-01
相关资源
最近更新 更多