在编译器浏览器(以及阅读 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());
最佳答案
让我们简单一点:
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 notT
.
在这里,T
是size_t
(它是 optional
特化的模板参数)和 U
是参数类型。在原来的情况下,index = 1
, U
是int
这使得第二个项目符号保持不变( 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/