27

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)を使用してインスタンス化すると、referenceconst_referenceは同じタイプであるため、宣言が複製されます。(定義の本体は、その価値については同じです。)したがって、アロケーター対応のコンテナーは、明示的にconst値の型を処理できません。const内部を非表示にtupleすると、アロケータをインスタンス化できます。標準からのこのアロケータ要件は、の問題に関する少なくとも2つの古いlibstdc ++バグを閉じることを正当化するために使用されましたが std::vector<const int>、原則として私にはわかりません。また、libc ++は、明らかに単純な方法で問題を回避します。これはallocator<const T>、重複する関数宣言を削除しての特殊化を提供することです。

4

2 に答える 2

12

C ++ 03ではAssignable、§23.1/4の表64で定義されています。

    式戻りタイプ事後条件
    t = u T&tはuと同等です

一方では、この要件は満たされていませんでしたstd::map。一方で、の要件は厳しすぎましたstd::list。そして、C ++ 11は、一般的には必要ではないstd::vectorが、特定の操作(割り当てなど)の使用によって課せられることを示しました。

C ++ 11では、対応する要件に名前が付けられCopyAssignable、§17.6.3.1/2の表23で定義されています。

    式戻りタイプ戻り値事後条件
    t = v T&ttはvと同等です。
                                                 vの値は変更されません

主な違いは、コンテナ要素が必要でなくなったことと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.
}

Visual C ++ 11.0を使用したビルド:

[D:\ dev \ test \ so \assignable]
> classignable.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:代わりに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:'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:'struct std::is_assignable'から必須
assignable.cpp:10:59:'bool isAssignable()[with Type=B]'から必要
assignable.cpp:16:32:ここから必要
assignable.cpp:7:24:エラー:'B&B :: operator =(const B&)' is private
d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 /ビット/move.h:57:0、
                 d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_pair .h:61、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_algobase .h:65、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits/char_traitsから.h:41、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ios:41から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ostream:40から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / iostream:40から、
                 assignable.cpp:1から:
d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / type_traits:1049: 2:エラー:このコンテキスト内
d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / type_traits:代わりに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:'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:'struct std::is_assignable'から必須
assignable.cpp:10:59:'bool isAssignable()[with Type=B]'から必要
assignable.cpp:16:32:ここから必要
assignable.cpp:7:24:エラー:'B&B :: operator =(const B&)' is private
d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 /ビット/move.h:57:0、
                 d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_pair .h:61、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_algobase .h:65、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits/char_traitsから.h:41、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ios:41から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ostream:40から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / iostream:40から、
                 assignable.cpp:1から:
d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / type_traits:1049: 2:エラー:このコンテキスト内
d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / type_traits:インスタンス化中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:'struct std::is_assignable'から必須
assignable.cpp:10:59:'bool isAssignable()[with Type=B]'から必要
assignable.cpp:16:32:ここから必要
assignable.cpp:7:24:エラー:'B&B :: operator =(const B&)' is private
d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 /ビット/move.h:57:0、
                 d:\ bin \ mingw \ bin \ .. / lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_pair .h:61、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits / stl_algobase .h:65、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / bits/char_traitsから.h:41、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ios:41から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / ostream:40から、
                 d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / iostream:40から、
                 assignable.cpp:1から:
d:\ bin \ mingw \ bin \ ../ lib / gcc / i686-pc-mingw32 / 4.7.1 / .. / .. / .. / .. / include / c ++ / 4.7.1 / type_traits:1055: 68:エラー:このコンテキスト内

[D:\ dev \ test \ so \assignable]
> _

したがって、要約すると、この標準のstd::is_assignable有用性は非常に限られているようであり、この記事の執筆時点では、ポータブルコードに依存することはできません。


編集:<utility>正しいに置き換えられました<type_traits。興味深いことに、g++では問題ではありませんでした。エラーメッセージも出ていないので、そのままにしておきます。

于 2012-12-23T08:14:29.787 に答える
4

これをAlfに渡しますが、後で参照できるようにいくつかメモを追加したいと思います。

Alfが言うように、std::is_*_assignable実際には、適切な代入演算子の存在(明示的または暗黙的)のみをチェックしてください。インスタンス化された場合に整形式であるかどうかを確認する必要はありません。これは、デフォルトの代入演算子では正常に機能します。宣言されたメンバーがある場合const、デフォルトの代入演算子は削除されます。基本クラスに削除された代入演算子がある場合、デフォルトの代入演算子は削除されます。したがって、デフォルトに任せれば問題ありません。

ただし、宣言する場合operator=は、適切に削除されるようにする責任があります(気になる場合)。たとえば、これはコンパイルして実行し(少なくともclangを使用して)、Cis_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;
}

代入演算子の定義がないことは、リンク時まで通知されません。この場合、代入演算子は使用されないため、まったく通知されません。ただし、C::operator=条件付きの使用はstd::is_assignableコンパイルを許可されることに注意してください。もちろん、そのメンバーは割り当て可能ではないため、C::operator=そのメンバーに割り当てる結果となるような方法で定義することはできませんaでした。

しかし、それは特に興味深い例ではありません。興味深いのは、std::tupleこの質問全体を開始した問題などのテンプレートの使用です。C::operator=上記にいくつかのテンプレートを追加して、実際にそのメンバーへの割り当てを通じて定義してみましょうa

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;
}   

A<false>最後に割り当てがないと、コードはコンパイルおよび実行され(clang 3.3で)、割り当て可能ではない(正しい)が、C<false>割り当て可能である(驚きです!)と報告します。C::operator=コンパイラが実際にその演算子をインスタンス化しようとするのはその時点までではないため、実際に使用しようとすると真実が明らかになります。それまでは、のインスタンス化を通じて、is_assignableオペレーターはパブリックインターフェイスの宣言にすぎませんでした。これは、Alfが言うように、std::is_assignable実際に探しているものすべてです。

ふぅ。

operator=つまり、これは、コンポーネントタイプのいずれかを割り当てできない場合に削除する必要がある、標準の集計オブジェクトに関する標準ライブラリと標準ライブラリの両方の実装の欠陥だと思います。についてstd::tupleは、§20.4.2.2にoperator=、すべてのコンポーネントタイプが割り当て可能であるための要件が​​リストされており、他のタイプにも同様の要件がありますが、この要件では、ライブラリの実装者が該当しないものを削除する必要はないと思いますoperator=

しかし、私が見る限り、ライブラリの実装が削除を行うのを妨げるものは何もありません(条件付きで代入演算子を削除するという煩わしい要因を除いて)。私の意見では、これについて数日間取りつかれた後、彼らはそうするべきであり、さらに標準は彼らにそうすることを要求するべきです。is_assignableそうでなければ、不可能の信頼できる使用。

于 2012-12-23T16:43:14.340 に答える