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

alt*_*ter 7 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);
};
Run Code Online (Sandbox Code Playgroud)

我无法理解它是如何工作或如何评估的部分是size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true))我知道如果赋值操作失败,它将回退到返回false的has_assign的第一个定义,但我不知道为什么它有该, true)部分.

我使用在assign操作符上返回void的结构进行了一些测试,并删除了, true部分sizeof给出了相同的结果.

Rei*_*ica 7

原则上,表达式的类型std::declval<X>() = std::declval<Y>()(即所operator =涉及的返回类型)可以是任意的,包括不完整的类型或void.在这种情况下,SFINAE不会启动,因为表达式是有效的.但是,您应用于sizeof不完整类型时会收到错误.(请注意,某些编译器定义sizeof(void) == 1为扩展,但不能普遍依赖).

, true在SFINAE表达式之后添加通过丢弃赋值的类型(无论它是什么)以及应用于(这是完全有效的)sizeof来修复此问题true.

正如Barry在评论中指出的那样,更直接的方法是使用赋值类型decltype而不是in sizeof,如下所示:

template <class X, class Y, class S = decltype(std::declval<X>() = std::declval<Y>()) >
constexpr static bool has_assign(bool) { return true; }
Run Code Online (Sandbox Code Playgroud)


Bar*_*rry 7

要申请sizeof(),您需要一个完整的类型.但是返回完整类型不是可赋值的要求,因此:

sizeof((std::declval<X>() = std::declval<Y>(), true))
       ~~~~~~~~~~~~~~~~~~ expr ~~~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

如果赋值对这两种类型有效,那么我们sizeof(expr)的类型exprbool(因为true).因此,如果作业有效,我们会得到一些真实的size.否则,替换失败.


但这是编写此代码的一种不必要的神秘方式.而且,它甚至不正确,因为我可以写一个类似的类型:

struct Evil {
    template <class T> Evil operator=(T&& ); // assignable from anything
    void operator,(bool);                    // mwahahaha
};
Run Code Online (Sandbox Code Playgroud)

现在你sizeof()仍然无法正常工作.

相反,更喜欢简单:

class = decltype(std::declval<X>() = std::declval<Y>())
Run Code Online (Sandbox Code Playgroud)

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