ric*_*ici 26 c++ language-lawyer c++11
C++ 11删除了所有容器的值类型为CopyConstructible和Assignable的要求(尽管容器上的特定操作可能会强加这些要求).从理论上讲,这应该可以定义,例如std::deque<const Foo>
,哪些在C++ 03中是不可能的.
出乎意料的是,当我尝试这个时,gcc 4.7.2产生了它通常不可理解的错误[1],但是clang至少使错误可读并且与libc ++一起编译它没有错误.
现在,当两个不同的编译器产生不同的结果时,它总是让我想知道正确的答案是什么,所以我搜索了所有可以找到的const/assignable/value types/containers等等的引用.我发现差不多了十年来非常相似的问题和答案,其中一些在SO和其他C++邮件列表中的其他地方,包括Gnu buganizer,所有这些基本上可以概括为以下对话.
问:为什么我不能申报std::vector<const int>
(作为一个简化的例子)
答:你为什么要这样做呢?这是荒谬的.
问:嗯,这对我来说很有意义.为什么我不能这样做?
答:因为标准要求值类型可分配.
问:但我不打算分配它们.我希望它们在我创建它们之后成为const.
答:这不是它的工作方式.下一个问题!
温和的潇洒:
A2:C++ 11决定允许这样做.你只需要等待.在此期间,重新思考你的荒谬设计.
这些似乎不是非常引人注目的答案,虽然可能我有偏见,因为我属于"但它对我有意义"的范畴.在我的情况下,我想有一个类似堆栈的容器,其中被推入堆栈的东西是不可变的,直到它们被弹出,这不会让我感到特别奇怪的是希望能够用类型表达系统.
无论如何,我开始考虑答案,"标准要求所有容器的值类型可以分配." 而且,据我所知,现在我发现了C++ 03标准草案的旧版本,这是真的; 它做了.
在另一方面,价值型std::map
就是不看我喜欢它的分配.尽管如此,我还是再次尝试,然后gcc继续编写它而不用眨眼.所以至少我有一些解决方法.std::pair<const Key, T>
std::deque<std::tuple<const Foo>>
然后我试图打印出的价值std::is_assignable<const Foo, const Foo>
和std::is_assignable<std::tuple<const Foo>, const std::tuple<const Foo>>
,而且事实证明,前者被报告为不分配,如你所期望的,但后者报告为分配后(通过铛和gcc).当然,它不是真正可转让的; 尝试编译a = b;
被gcc拒绝投诉error: assignment of read-only location
(这只是我在此任务中遇到的唯一错误消息,实际上很容易理解).但是,如果没有尝试进行赋值,clang和gcc同样乐于实例化deque<const>
,并且代码似乎运行正常.
现在,如果std::tuple<const int>
真的是可分配的,那么我不能抱怨C++03
标准中的不一致- 而且,真的,谁在乎 - 但我发现令人不安的是,两个不同的标准库实现报告类型是可分配的,实际上,尝试分配给它的引用将导致(非常明智的)编译器错误.我可能在某些时候想要在模板SFINAE中使用该测试,并根据我今天看到的,它看起来不太可靠.
那么有没有人能够对这个问题有所了解(标题中):Assignable究竟意味着什么?还有两个奖励问题:
1)委员会是否真的意味着允许实例化具有const
值类型的容器,或者他们是否考虑了其他一些不可分配的案例?
2)是否有真正的constnesses之间的差异显著const Foo
和std::tuple<const Foo>
?
[1]对于真正的好奇,这里是gcc在尝试编译声明时产生的错误消息std::deque<const std::string>
(添加了一些行结尾,如果你向下滚动得足够远,则说明一下):
In file included from /usr/include/x86_64-linux-gnu/c++/4.7/./bits/c++allocator.h:34:0,
from /usr/include/c++/4.7/bits/allocator.h:48,
from /usr/include/c++/4.7/string:43,
from /usr/include/c++/4.7/random:41,
from /usr/include/c++/4.7/bits/stl_algo.h:67,
from /usr/include/c++/4.7/algorithm:63,
from const_stack.cc:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘class __gnu_cxx::new_allocator<const std::basic_string<char> >’:
/usr/include/c++/4.7/bits/allocator.h:89:11: required from ‘class std::allocator<const std::basic_string<char> >’
/usr/include/c++/4.7/bits/stl_deque.h:489:61: required from ‘class std::_Deque_base<const std::basic_string<char>, std::allocator<const std::basic_string<char> > >’
/usr/include/c++/4.7/bits/stl_deque.h:728:11: required from ‘class std::deque<const std::basic_string<char> >’
const_stack.cc:112:27: required from here
/usr/include/c++/4.7/ext/new_allocator.h:83:7:
error: ‘const _Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> >::address(
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference) const [
with _Tp = const std::basic_string<char>;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_pointer =
const std::basic_string<char>*;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::const_reference =
const std::basic_string<char>&]’ cannot be overloaded
/usr/include/c++/4.7/ext/new_allocator.h:79:7:
error: with ‘_Tp* __gnu_cxx::new_allocator< <template-parameter-1-1> >::address(
__gnu_cxx::new_allocator< <template-parameter-1-1> >::reference) const [
with _Tp = const std::basic_string<char>;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::pointer = const std::basic_string<char>*;
__gnu_cxx::new_allocator< <template-parameter-1-1> >::reference = const std::basic_string<char>&]’
Run Code Online (Sandbox Code Playgroud)
那么这里发生的是标准(第20.6.9.1节)坚持默认分配器具有成员函数:
pointer address(reference x) const noexcept;
const_pointer address(const_reference x) const noexcept;
Run Code Online (Sandbox Code Playgroud)
但如果你用它实例化const
模板参数(这显然是UB),然后reference
和const_reference
属于同一类型,因此声明被复制.(定义的主体是相同的,因为它的价值.)因此,没有分配器感知的容器可以处理显式const
值类型.隐藏const
内部tuple
允许分配器实例化.标准中的这个分配器要求被用来证明关闭至少几个关于问题的旧libstdc ++错误是合理的 std::vector<const int>
,尽管它并没有把我当作一个坚实的原则.此外,libc ++以明显简单的方式解决问题,即提供allocator<const T>
删除了重复函数声明的特殊化.
Che*_*Alf 12
在C++ 03中,Assignable
由§23.1/ 4中的表64定义,
Expression Return type Post-condition t = u T& t is equivalent to u
一方面,这个要求没有得到满足std::map
.另一方面,它的要求太严格了std::list
.而C++ 11证明,它通常不是必需的std::vector
,而是通过使用某些操作(例如赋值)强加的.
在C++ 11中,相应的需求被命名CopyAssignable
,并由§17.6.3.1/ 2中的表23定义,
Expression Return type Return value Post-condition t = v T& t t is equivalent to v, the value of v is unchanged
主要区别在于容器元素不再需要CopyAssignable
,并且存在相应的要求MoveAssignable
.
无论如何,具有const
数据成员的结构显然不可分配,除非选择用非常特殊的解释来阅读"等同于".
对于C++ 11中唯一与操作无关的元素类型要求,就我所见(来自§23.2.1/ 4中的表96)而言,它必须是Destructible
.
关于std::is_assignable
,它没有完全测试CopyAssignable
标准.
std::is_assignable<T, U>
根据C++11§20.9.4.3/ 3中的表49,这意味着:
"
declval<T>() = declval<U>()
当作为未评估的操作数处理时,表达 形式良好(第5条).执行访问检查就像在与T
和无关的上下文中一样U
.仅考虑赋值表达式的直接上下文的有效性.[ 注意:表达式的编译可能会导致副作用,例如类模板特化和函数模板特化的实例化,隐式定义函数的生成等等.这种副作用不在"直接背景"中,并且可能导致程序形成不良.- 后注 ]"
本质上,这意味着访问/存在+参数类型兼容性检查operator=
,仅此而已.
但是,Visual C++ 11.0似乎没有进行访问检查,而g ++ 4.7.1对它进行了扼流:
#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.
}
Run Code Online (Sandbox Code Playgroud)
使用Visual C++ 11.0构建:
[D:\dev\test\so\assignable] > cl assignable.cpp assignable.cpp [D:\dev\test\so\assignable] > _
使用g ++ 4.7.1构建:
[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] > _
因此,总而言之,标准std::is_assignable
似乎是非常有限的实用性,并且在撰写本文时,它不能依赖于可移植代码.
编辑:替换<utility>
为正确<type_traits
.有趣的是,它与g ++无关.甚至没有错误信息,所以我只是让它成为现实.