c++ - 在这个简单的例子中,为什么 std::optional 的赋值运算符不能用于编译时上下文?

标签 c++ templates c++20 constexpr stdoptional

在编译器浏览器(以及阅读 cppref.com 上的 std::optional)半小时后,我放弃了。除了我不明白为什么这段代码不能编译之外,没什么好说的。有人请解释一下,如果有的话,也许可以告诉我一种解决方法? 我在这里使用的 std::optional 的所有成员函数都是 constexpr,并且确实应该在编译时可计算,因为这里的可选类型 - size_t - 是原始标量类型。

#include <type_traits>
#include <optional>

template <typename T>
[[nodiscard]] constexpr std::optional<size_t> index_for_type() noexcept
{
    std::optional<size_t> index;
    if constexpr (std::is_same_v<T, int>)
        index = 1;
    else if constexpr (std::is_same_v<T, void>)
        index = 0;

    return index;
}
 
static_assert(index_for_type<int>().has_value());

https://godbolt.org/z/YKh5qT4aP

最佳答案

让我们简单一点:

constexpr std::optional<size_t> index_for_type() noexcept
{
    std::optional<size_t> index;
    index = 1;
    return index;
}

static_assert(index_for_type().has_value());

index = 1;你试图在 assignment operators 中调用的候选人是#4:

template< class U = T >
optional& operator=( U&& value );

请注意,这个候选不是最初制作的 constexpr在 C++20 中,它是最近的 DR ( P2231R1 )。 libstdc++ 尚未实现此更改,这就是您的示例无法编译的原因。到目前为止,它是完全正确的 C++20 代码。图书馆还没有完全跟上。


Marek's 的原因建议作品:

constexpr std::optional<size_t> index_for_type() noexcept
{
    std::optional<size_t> index;
    index = size_t{1};
    return index;
}

static_assert(index_for_type().has_value());

是因为不是调用赋值运算符#4 (否则出于同样的原因它会继续不起作用,只是在这个实现中还不是 constexpr),它切换到调用运算符 #3 (即 constexpr ):

constexpr optional& operator=( optional&& other ) noexcept(/* see below */);

为什么是这个?因为#4有这个约束:

and at least one of the following is true:

  • T is not a scalar type;
  • std::decay_t<U> is not T.

在这里,Tsize_t (它是 optional 特化的模板参数)和 U是参数类型。在原来的情况下,index = 1 , Uint这使得第二个项目符号保持不变( int 实际上不是 size_t )因此这个赋值运算符是有效的。但是当我们把它改成index = size_t{1} , 现在 U变成 size_t , 所以第二个项目符号也是假的,我们失去了这个赋值运算符作为候选。

这留下了复制分配和移动分配作为候选,后者更好。移动任务 constexpr在此实现中,它有效。


当然,更好的实现仍然是避免赋值并且只是:

constexpr std::optional<size_t> index_for_type() noexcept
{
    return 1;
}

static_assert(index_for_type().has_value());

或者,在原来的函数中:

template <typename T>
[[nodiscard]] constexpr std::optional<size_t> index_for_type() noexcept
{
    if constexpr (std::is_same_v<T, int>) {
        return 1;
    } else if constexpr (std::is_same_v<T, void>) {
        return 0;
    } else {
        return std::nullopt;
    }
}

这工作得很好,即使在 C++17 中也是如此。

关于c++ - 在这个简单的例子中,为什么 std::optional 的赋值运算符不能用于编译时上下文?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68230863/

相关文章:

html - 在 "Miniport"html5 模板中居中 DIV

c++ - 您如何逆转strong_ordering?

c++ - 如何使用 xcb 正确设置 utf8 窗口标题?

c++ - 为什么免费调用两次时会崩溃?

c++ - 使用 const_iterator 如何编译出更高效的程序?

django - 防止 Django 模板中的整数出现 NUMBER_GROUPING

c++ - 函数模板的部分排序和非推导上下文在 MSVC 2017 中不起作用

c++ - contiguous_range始终是size_range吗?

c++ - 我如何让这个 C++ 计算程序留在控制台上?

c++ - isalpha : If argv is not alphabets. 如何调节?