C ++ 11は、すべてのコンテナーの値型がCopyConstructibleおよびAssignableであるという要件を削除しました(ただし、コンテナーに対する特定の操作によってこれらの要件が課される場合があります)。理論的には、これにより、たとえば、std::deque<const Foo>
C++03では不可能だったを定義できるようになります。
予期せぬことに、gcc 4.7.2は、これを試したときに通常の理解できないエラー[1]を生成しましたが、clangは少なくともエラーを読み取り可能にし、libc++を使用したclangはエラーなしでコンパイルしました。
さて、2つの異なるコンパイラが異なる結果を生成するとき、それは常に正しい答えが何であるか疑問に思うので、私はconst/assignable/valuetypes/containersなどを見つけることができるすべての参照を検索しました。 10年分の非常によく似た質問と回答があり、そのうちのいくつかはSOにあり、その他はさまざまなC ++メーリングリストにあり、Gnu Buganizerを含む他の場所もあります。これらはすべて、基本的に次のダイアログとして要約できます。
Q:なぜ宣言できないのですかstd::vector<const int>
(簡単な例として)
A:いったいなぜそんなことをしたいのですか?それは無意味です。
Q:それは私には理にかなっています。なぜできないのですか?
A:標準では、値の型を割り当て可能にする必要があるためです。
Q:しかし、私はそれらを割り当てる予定はありません。作成した後は、constにしてほしいです。
A:それはそれが機能する方法ではありません。次の問題!
軽度のダッシュで:
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>>
してみました。予想どおり、前者は割り当て不可として報告されていますが、後者は(clangとgccの両方で)割り当て可能として報告されています。もちろん、それは実際には割り当て可能ではありません。コンパイルしようとするa = b;
と、gccによって苦情が出されて拒否されますerror: assignment of read-only location
(これは、このクエストで遭遇した、実際には理解しやすい唯一のエラーメッセージでした)。ただし、割り当てを行わなくても、clangとgccの両方が同じようにインスタンス化deque<const>
でき、コードは正常に実行されているようです。
さて、std::tuple<const int>
本当に割り当て可能であれば、C++03
標準の不一致について文句を言うことはできません-そして、実際には誰が気にしますか-しかし、2つの異なる標準ライブラリの実装が実際にはタイプが割り当て可能であると報告するのは気がかりです、その参照に割り当てようとすると、(非常に賢明な)コンパイラエラーが発生します。ある時点で、テンプレートSFINAEでテストを使用したいと思うかもしれませんが、今日見たものに基づくと、信頼性はあまり高くありません。
それで、(タイトルの)質問に光を当てることができる人はいますか:Assignableは実際にはどういう意味ですか?そして2つのボーナス質問:
1)委員会は、値型を使用してコンテナーをインスタンス化できるようにすることを本当に意味していましたconst
か、それとも他の割り当て不可能なケースを念頭に置いていましたか?
2)との恒常性には本当に大きな違いが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>&]’
したがって、ここで起こっていることは、標準(§20.6.9.1)がデフォルトのアロケータにメンバー関数があることを要求しているということです。
pointer address(reference x) const noexcept;
const_pointer address(const_reference x) const noexcept;
const
ただし、テンプレート引数(明らかにUB)を使用してインスタンス化すると、reference
とconst_reference
は同じタイプであるため、宣言が複製されます。(定義の本体は、その価値については同じです。)したがって、アロケーター対応のコンテナーは、明示的にconst
値の型を処理できません。const
内部を非表示にtuple
すると、アロケータをインスタンス化できます。標準からのこのアロケータ要件は、の問題に関する少なくとも2つの古いlibstdc ++バグを閉じることを正当化するために使用されましたが std::vector<const int>
、原則として私にはわかりません。また、libc ++は、明らかに単純な方法で問題を回避します。これはallocator<const T>
、重複する関数宣言を削除しての特殊化を提供することです。