Constexpr成员函数

时间:2019-11-14 21:15:29

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

假设我有一个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()中定义S成员函数,如果引擎的constexprsize(),则可以用作constexpr。我写道:

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

(如果此代码在这两个标准中具有不同的有效性,我会同时用标记这个问题。)

3 个答案:

答案 0 :(得分:5)

代码写得很好。

  

[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函数有效,只需要存在一组 some 个参数,评估可为其生成一个有效的常数表达式。如以下示例表格中的标准所示:

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
};

答案 1 :(得分:2)

是的

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

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

答案 2 :(得分:0)

不是直接答案,而是另一种方法:

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);
}