这个sizeof表达式如何评估?为什么这样称呼?

时间:2016-10-28 14:20:17

标签: c++

我在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部分给出了相同的结果。

2 个答案:

答案 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()

这可以实现相同的结果 - 替换失败与否 - 无需关心结果的类型或处理特殊情况。