C++ 什么是;“可转让”;真的吗?

C++ 什么是;“可转让”;真的吗?,c++,c++11,language-lawyer,C++,C++11,Language Lawyer,C++11删除了所有容器的值类型都是可复制和可分配的要求(尽管容器上的特定操作可能会强制这些要求)。理论上,这应该可以定义,例如,std::deque,这在C++03中是不可能的 出乎意料的是,当我尝试这一点时,GCC4.7.2产生了通常令人费解的错误[1],但clang至少使错误可读,并且clang使用libc++编译时没有错误 现在,当两个不同的编译器产生不同的结果时,我总是想知道正确的答案是什么,所以我搜索了所有我能找到的关于常量/可赋值/值类型/容器等的参考文献。我发现了近十年来非常相似

C++11删除了所有容器的值类型都是可复制和可分配的要求(尽管容器上的特定操作可能会强制这些要求)。理论上,这应该可以定义,例如,
std::deque
,这在C++03中是不可能的

出乎意料的是,当我尝试这一点时,GCC4.7.2产生了通常令人费解的错误[1],但clang至少使错误可读,并且clang使用libc++编译时没有错误

现在,当两个不同的编译器产生不同的结果时,我总是想知道正确的答案是什么,所以我搜索了所有我能找到的关于常量/可赋值/值类型/容器等的参考文献。我发现了近十年来非常相似的问题和答案,其中一些在SO和其他C++的邮件列表中,包括GNU BugGaNER,其他基本上可以概括为以下对话。 问:为什么我不能声明
std::vector
(作为一个简化示例)

你到底为什么要那样做?这是荒谬的

问:嗯,这对我来说很有意义。为什么我不能做呢

答:因为标准要求值类型是可赋值的

问:但我不打算分配他们。我希望在我创建它们之后它们是常量

答:不是这样的。下一个问题

略带几分轻快:

A2:C++11已经决定允许这样做。你只需等待。同时,重新思考你的荒谬设计

这些似乎不是很有说服力的答案,尽管我可能有偏见,因为我属于“但对我来说有意义”的范畴。在我的例子中,我希望有一个类似堆栈的容器,在这个容器中,推到堆栈上的东西在弹出之前是不可变的,我并不认为这是一件特别奇怪的事情,因为我希望能够用类型系统来表达

无论如何,我开始思考答案,“标准要求所有容器的值类型都是可赋值的。”而且,就我所见,现在我找到了C++03标准草案的旧副本,这是真的;是的

另一方面,
std::map
的值类型是
std::pair
,在我看来它不是可赋值的。尽管如此,我还是再次尝试使用
std::deque
,gcc继续编译它,没有眨眼。所以至少我有一些解决办法

然后我尝试打印出
std::is_assignable
std::is_assignable
的值,结果发现,正如您所期望的,前者报告为不可分配,但后者报告为可分配(通过clang和gcc)。当然,它不是真正的可分配的;试图编译
a=b
被gcc拒绝,投诉为
错误:分配只读位置
(这几乎是我在这个任务中遇到的唯一一条易于理解的错误消息)。但是,如果不尝试进行赋值,clang和gcc都同样乐于实例化
deque
,代码似乎运行良好

现在,如果
std::tuple
确实是可分配的,那么我不能抱怨
C++03
标准中的不一致性——而且,真的,谁在乎呢——但我发现两个不同的标准库实现报告一个类型是可分配的,而实际上,尝试分配给它的引用将导致(非常合理)编译器错误。我可能在某个时候想在模板SFINAE中使用该测试,根据我今天看到的,它看起来不太可靠

那么,有没有人能解释一下这个问题(在标题中):可分配的真正含义是什么?还有两个额外的问题:

1) 委员会是否真的打算允许实例化具有
const
值类型的容器,还是考虑到了其他一些不可分配的情况

2)
constfoo
std::tuple
的常量之间真的存在显著差异吗


[1] 对于真正好奇的人来说,这里是gcc在试图编译
std::deque
声明时产生的错误消息(添加了一些行尾,如果向下滚动足够远,还有一个解释):


但是如果使用
const
模板参数(显然是UB)实例化它,那么
reference
const\u reference
是相同的类型,因此声明是重复的。(定义的主体是相同的,不管它值多少。)因此,没有一个支持分配器的容器可以处理显式的
const
值类型。将
常量
隐藏在
元组中
允许分配器实例化。标准中的这个分配器要求被用来证明至少关闭了两个关于
std::vector
问题的旧libstdc++bug是合理的,尽管我并不认为这是一个可靠的原则。此外,libc++以一种显而易见的简单方式解决了这个问题,即在删除重复函数声明的情况下,提供一个专门化的
分配器。

在C++03中,
可分配的
由§23.1/4中的表64定义

Expression Return type Post-condition t = u T& t is equivalent to u 主要区别在于,容器元素不再需要是
可复制分配的
,并且有相应的要求
可移动分配的

无论如何,具有
const
数据成员的结构显然是不可赋值的,除非选择使用非常特殊的解释来读取“等价于”

据我所知(从§23.2.1/4中的表96),C++11中唯一与操作无关的元素类型要求是它必须是可破坏的


关于
std::is_assignable
,它没有完全测试
copysignable
标准
pointer address(reference x) const noexcept;
const_pointer address(const_reference x) const noexcept;
Expression Return type Post-condition t = u T& t is equivalent to u Expression Return type Return value Post-condition t = v T& t t is equivalent to v, the value of v is unchanged
#include <iostream>
#include <type_traits>
#include <tuple>
using namespace std;

struct A {};
struct B { private: B& operator=( B const& ); };

template< class Type >
bool isAssignable() { return is_assignable< Type, Type >::value;  }

int main()
{
    wcout << boolalpha;
    wcout << isAssignable< A >() << endl;              // OK.
    wcout << isAssignable< B >() << endl;              // Uh oh.
}
[D:\dev\test\so\assignable] > cl assignable.cpp assignable.cpp [D:\dev\test\so\assignable] > _ [D:\dev\test\so\assignable] > g++ assignable.cpp d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits: In substitution of 'template static decltype (((declval)()=(declval)(), std::__sfinae_types::__one()))std::__is_assignable_helper::__test(int) [with _ Tp1 = _Tp1; _Up1 = _Up1; _Tp = B; _Up = B] [with _Tp1 = B; _Up1 = B]': d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1055:68: required from 'constexpr const bool std::__is_assignable_helper::value' d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1060:12: required from 'struct std::is_assignable' assignable.cpp:10:59: required from 'bool isAssignable() [with Type = B]' assignable.cpp:16:32: required from here assignable.cpp:7:24: error: 'B& B::operator=(const B&)' is private In file included from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/move.h:57:0, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_pair.h:61, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_algobase.h:65, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/char_traits.h:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ios:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ostream:40, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/iostream:40, from assignable.cpp:1: d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1049:2: error: within this context d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits: In substitution of 'template static decltype (((declval)()=(declval)(), std::__sfinae_types::__one())) std::__is_assignable_helper::__test(int) [with _ Tp1 = _Tp1; _Up1 = _Up1; _Tp = B; _Up = B] [with _Tp1 = B; _Up1 = B]': d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1055:68: required from 'constexpr const bool std::__is_assignable_helper::value' d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1060:12: required from 'struct std::is_assignable' assignable.cpp:10:59: required from 'bool isAssignable() [with Type = B]' assignable.cpp:16:32: required from here assignable.cpp:7:24: error: 'B& B::operator=(const B&)' is private In file included from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/move.h:57:0, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_pair.h:61, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_algobase.h:65, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/char_traits.h:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ios:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ostream:40, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/iostream:40, from assignable.cpp:1: d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1049:2: error: within this context d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits: In instantiation of 'constexpr const bool std::__is_assignable_helper::value': d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1060:12: required from 'struct std::is_assignable' assignable.cpp:10:59: required from 'bool isAssignable() [with Type = B]' assignable.cpp:16:32: required from here assignable.cpp:7:24: error: 'B& B::operator=(const B&)' is private In file included from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/move.h:57:0, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_pair.h:61, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/stl_algobase.h:65, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/bits/char_traits.h:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ios:41, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/ostream:40, from d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/iostream:40, from assignable.cpp:1: d:\bin\mingw\bin\../lib/gcc/i686-pc-mingw32/4.7.1/../../../../include/c++/4.7.1/type_traits:1055:68: error: within this context [D:\dev\test\so\assignable] > _
#include <iostream>
#include <type_traits>
#include <tuple>
using namespace std;

struct A { const int x; A() : x() {}};

struct C { 
  struct A a;
  C& operator=( C const& other);
};

template< class Type >
bool isAssignable() { return is_assignable< Type&, const Type& >::value;  }

int main()
{
    wcout << boolalpha;
    wcout << isAssignable< A >() << endl; // false
    wcout << isAssignable< C >() << endl; // true
    C c1;
    C c2;
}
using namespace std;

template<bool> struct A {
  A() : x() {}
  const int x;
};

template<bool B> struct C {
  struct A<B> a;
  C& operator=( C const& other) {
    this->a = other.a;
    return *this;
  }
};  

template< class Type >
bool isAssignable() { return is_assignable< Type&, const Type& >::value;  }

int main()
{   
    wcout << boolalpha;
    wcout << isAssignable< A<false> >() << endl; // false
    wcout << isAssignable< C<false> >() << endl; // true
    C<false> c1;
    C<false> c2;
    c1 = c2;                                     // Bang
    return 0;
}