【问题标题】:What is Allowed in a constexpr Function?constexpr 函数中允许什么?
【发布时间】:2017-05-27 21:05:45
【问题描述】:

constexpr functions 不应包含:

非文字类型变量的定义

但在这个答案中,一个 lambda 被定义在一个:https://stackoverflow.com/a/41616651/2642059

template <typename T>
constexpr auto make_div(const T quot, const T rem)
{
    return [&]() {
        decltype(std::div(quot, rem)) result;
        result.quot = quot;
        result.rem = rem;
        return result;
    }();
}

在我的评论中,我将div_t 定义为一个:How can I Initialize a div_t Object?

template <typename T>
constexpr decltype(div(T{}, T{})) make_div(const T quot, const T rem)
{
    decltype(div(T{}, T{})) x{};
    x.quot = quot;
    x.rem = rem;
    return x;
}

禁止“定义非文字类型的变量”究竟是什么意思?

Visual Studio 2015 不允许我定义 div_t,但我发现允许将这种非法行为包装在 lambda 中并执行它是荒谬的。我想知道哪个编译器在 div_t 定义方面的行为是否正确。

【问题讨论】:

  • 什么版本的 C++ 语言?与 2011 年相比,要求放宽了很多。
  • @rubenvb 好问题,我已经编辑过了。但是 C++14。
  • 一个 lambda 表达式是一个表达式,而不是一个变量定义。但在 C++17 之前,lambda 表达式不能出现在 constant expression 中。确实,when the context requires a constant expression, MSVC will complain.
  • @cpplearner 所以我的(第一个)示例仅在 constexpr 函数被评估为 const 函数时编译?在我看来,第二个例子不应该按定义编译。
  • 我认为 VS2015 不支持 C++14 扩展 constexpr。这解释了您的第二个函数模板,因为在 C++11 中 constexpr 函数基本上仅限于单个 return 语句;它与“非文字类型”无关(div_t 是文字类型)。第一个是格式不正确的 NDR pre-C++17。

标签: c++ function c++14 constexpr c++17


【解决方案1】:

几乎可以保证,如果存在差异 gcc 具有正确的行为,因为 Visual Studio 2015 不支持 对 constexpr 的扩展:https://msdn.microsoft.com/en-us/library/hh567368.aspx#C-14-Core-Language-Features

C++11 constexpr 函数

函数体只能包含:

  • 空语句(纯分号)
  • static_assert 声明
  • typedef 未定义类或枚举的声明和别名声明
  • using 声明
  • using 指令
  • 正好是一个return 声明

所以 不能容忍decltype(div(T{}, T{})) x{} 的定义。然而,在 constexpr 函数中滚动建议的三元 here 以达到相同的结果是可以接受的:

template <typename T>
constexpr auto make_div(const T quot, const T rem)
{
    using foo = decltype(div(T{}, T{}));
                         
    return foo{1, 0}.quot != 0 ? foo{quot, rem} : foo{rem, quot};
}

Live Example

C++14 constexpr 函数

函数体可能包含除之外的任何内容:

  • asm 声明
  • goto 语句
  • 带有除 case 和 default 之外的标签的语句
  • 一个尝试块
  • 非文字类型变量的定义
  • 静态或线程存储持续时间变量的定义
  • 不进行初始化的变量定义

其中定义了“文字类型”here,但专门针对对象,它们可能是带有微不足道的析构函数的聚合类型。所以div_t 绝对合格。因此 以及扩展的 gcc 可以容忍decltype(div(T{}, T{})) x{} 的定义。

C++17 constexpr 函数

C++17 在“Literal Type”的定义中添加了对闭包类型的支持,所以我觉得奇怪的是 gcc 和 Visual Studio 都支持在 return 语句中使用 lambda。我猜这要么是前瞻性支持,要么是编译器选择内联 lambda。在任何一种情况下,我都不认为它可以作为 constexpr 函数。

[Source]

【讨论】:

  • @jnoathan_mee "函数体可能包含除...以外的任何内容,我们不能在函数体中调用非 constexpr 函数,对吧?
  • @camino 正确,此函数的结果必须在编译时可计算,因此不能满足此要求的函数或参数是不可接受的。尽管这一点并没有明确记录在案:(
猜你喜欢
  • 2017-12-29
  • 1970-01-01
  • 2015-02-19
  • 2021-10-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-16
  • 1970-01-01
相关资源
最近更新 更多