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