【问题标题】:Why is std::is_assignable counter-intuitive?为什么 std::is_assignable 违反直觉?
【发布时间】:2013-11-12 03:14:43
【问题描述】:

std::is_assignable<int, int>::value == false 在符合标准的实现中(例如 clang/libc++、gcc/libstdc++,但不是 VS2012)。

直观地说,这意味着 int x = 3; 这样的表达式是无效的。但是is_assignable 的规范声明赋值的两边都转换为std::add_rvalue_reference<T>::type,因此std::is_assignable<int, int>::value 必须计算为false(因为int + && -> int&&,这是一个不可赋值的右值)。

为什么std::is_assignable 是这样设计的,还是我误解了is_assignable<int, int>::value 的真正含义?

相关讨论:

【问题讨论】:

  • srd::is_assignable<int&,int>::value 然而,true... 问题不在于构造,而在于赋值。
  • int x = 3; 不是分配,对吗?

标签: c++ c++11 typetraits


【解决方案1】:

在这些特征中,T 不是左值引用类型,T 意味着右值。

对于许多用户定义的类型T,分配给右值类型是完全合理的。它在某些情况下甚至非常有用:

std::vector<bool> v(5);
v[0] = true;

在上面的表达式中,v[0] 是一个被赋值的右值。如果 vector&lt;bool&gt; 是一个糟糕的例子,那么下面的新 C++11 代码也是如此:

#include <tuple>

std::tuple<int, int>
do_something();

int
main()
{
    int i, j;
    std::tie(i, j) = do_something();
}

在上面,do_something() 的结果被分配给一个右值 std::tuple。赋值给右值很有用,甚至很常见,尽管在大多数赋值用法中都没有这样做。

所以std::is_assignable 允许确定能够分配给右值和左值之间的区别。如果您需要了解其中的区别,std::is_assignable 可以为您完成这项工作。

如果您正在处理更常见的情况,例如只是试图确定类型 T 是否可复制分配,则使用 is_copy_assignable&lt;T&gt;。这个 trait 按字面意思定义为 is_assignable 并将 lhs 强制为左值:

is_copy_assignable<T> == is_assignable<T&, const T&>

所以std::is_copy_assignable&lt;int&gt;::value 将按预期为真。

使用is_copy_assignable 作为您的首选,如果您也需要,请使用is_move_assignable。只有当这些特征对您不起作用时(可能是因为您需要查看异构分配),您才应该恢复直接使用is_assignable。然后您需要处理是否要在 lhs 上允许右值的问题,以便考虑可能涉及 vector&lt;bool&gt;::referencetuple 引用的情况。您必须明确选择是否要在 is_assignable 查询中允许此类情况。

例如:

#include <type_traits>
#include <vector>

int
main()
{
    static_assert(std::is_assignable<std::vector<bool>::reference&, bool>(),
        "Should be able to assign a bool to an lvalue vector<bool>::reference");

    static_assert(std::is_assignable<std::vector<bool>::reference, bool>(),
        "Should be able to assign a bool to an rvalue vector<bool>::reference");

    static_assert(std::is_assignable<bool&, std::vector<bool>::reference>(),
        "Should be able to assign a vector<bool>::reference to an lvalue bool");

    static_assert(!std::is_assignable<bool, std::vector<bool>::reference>(),
        "Should not be able to assign a vector<bool>::reference to an rvalue bool");
}

【讨论】:

  • 您所说的暗示实际上名称 is_assignable 具有误导性,如果命名为 is_rvalue_assignable 可能更直观。不过,感谢您对这种情况的良好解释。
  • is_copy_assignable 只接受一个模板参数 T,因此似乎不是 is_assignable 的左值模拟。似乎没有类型特征可以测试两种类型是否真正可分配。一般来说,这对用户来说似乎比只有一个检查右值可分配性的版本更有用。
  • @ThreeBit: vector&lt;bool&gt;::reference 是类类型,而不是bool&amp;。我添加了使用这两种不同类型的 is_assignable 的明确示例。
  • @ThreeBit:这里的问题是 C++ 有一个叫做“引用折叠”的东西。它会导致一些令人困惑的情况,但最终结果是std::add_rvalue_reference_t&lt;T&gt; 并不总是产生右值引用。如果类型是纯右值(它没有引用限定符,int)或者如果类型已经是右值引用(int &amp;&amp;),则结果是右值引用。但是,如果该类型是左值引用 (int &amp;),则它的计算结果为 int &amp; &amp;&amp;,它会折叠为 int &amp;
【解决方案2】:

std::is_assignable&lt;int, int&gt;::value == false 表示“不能将int 文字分配给int 文字”(除其他外)。

您的声明int x = 3std::is_assignable&lt;int&amp;, int&gt;::value

更多信息: http://en.cppreference.com/w/cpp/types/is_assignable

【讨论】:

    【解决方案3】:

    一个务实的答案是你可以通过修改你的左参数来得到你通常想要的东西。

    查询std::is_assignable_t&lt;T&amp;,U&gt;会告诉你是否可以合理地编写代码,例如:

    T t;
    U u;
    t = u;
    

    我说这是务实的,因为如果他们编写 std::is_assignable&lt;&gt; 以使用左侧的内置左值引用,那么重用此替代方案以获得其当前功能将更加困难。如果用户在外部添加一个任意类型的左值引用(它不会被擦除),这比在外部需要一个右值引用来进行不同的查询(很容易被擦除)更简单。

    所以,这个版本的std::is_assignable&lt;&gt; 将回答您典型的“从右值接收左值”赋值测试,但也会回答更罕见的查询。

    这种灵活性的代价是您描述的违反直觉的性质。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-07-22
      • 2020-03-21
      • 2015-04-12
      • 1970-01-01
      • 1970-01-01
      • 2019-02-25
      • 2019-05-29
      • 2013-09-06
      相关资源
      最近更新 更多