我在std::optional
实施中遇到了这段代码:
template <class T, class U>
struct is_assignable
{
template <class X, class Y>
constexpr static bool has_assign(...) { return false; }
template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) >
// the comma operator is necessary for the cases where operator= returns void
constexpr static bool has_assign(bool) { return true; }
constexpr static bool value = has_assign<T, U>(true);
};
我无法理解它是如何工作或如何评估的部分是size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true))
我知道如果分配操作失败,它将回退到返回false的第一个has_assign定义,但我不会#39 ; t知道为什么它有, true)
部分。
我使用在assign运算符上返回void的结构进行了一些测试,删除, true
中的sizeof
部分给出了相同的结果。
答案 0 :(得分:7)
原则上,表达式std::declval<X>() = std::declval<Y>()
的类型(即所涉及的operator =
的返回类型)可以是任意的,包括不完整的类型或void
。在这种情况下,SFINAE不会启动,因为表达式是有效的。但是,您将sizeof
应用于不完整类型时会出错。 (请注意,某些编译器将sizeof(void) == 1
定义为扩展名,但不能普遍依赖它。)
在SFINAE表达式之后添加, true
通过放弃分配的类型(无论是什么)来修复此问题,并将sizeof
应用于true
(这完全有效)。< / p>
正如评论中的Barry所示,更直接的方法是在decltype
而不是sizeof
中使用作业类型,如下所示:
template <class X, class Y, class S = decltype(std::declval<X>() = std::declval<Y>()) >
constexpr static bool has_assign(bool) { return true; }
答案 1 :(得分:7)
要申请sizeof()
,您需要一个完整的类型。但是返回完整类型并不是可分配性的要求,因此:
sizeof((std::declval<X>() = std::declval<Y>(), true))
~~~~~~~~~~~~~~~~~~ expr ~~~~~~~~~~~~~~~~~~~~~
如果作业对这两种类型有效,那么我们sizeof(expr)
的{{1}}类型为expr
(因为bool
)。因此,如果作业有效,我们会得到一些真实的true
。否则,替换失败。
但这是编写此代码的一种不必要的神秘方式。而且,它甚至不正确,因为我可以编写类似的类型:
size
现在您的struct Evil {
template <class T> Evil operator=(T&& ); // assignable from anything
void operator,(bool); // mwahahaha
};
仍无效。
相反,更喜欢简单:
sizeof()
这可以实现相同的结果 - 替换失败与否 - 无需关心结果的类型或处理特殊情况。