質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • C++

    4522questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • C++11

    122questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

  • C++14

    53questions

リスト初期化について

解決済

回答 3

投稿

  • 評価
  • クリップ 1
  • VIEW 1,905

JADEN

score 97

リスト初期化において、以下のことを教えてください。
引用サイト: C++11: Syntax and Feature

1) 2種類の直接初期化は、全く同じ意味か。

T x( { } ) ; // 直接初期化
T x{ } ; // 直接初期化


2) 直接初期化とコピー初期化の違いは何か。

T x( { } ) ; // 直接初期化
T x{ } ; // 直接初期化
T x = { } ; // コピー初期化


3) コピーコンストラクタがdeleteなのに、なぜ以下のコードが通るのか。

// コンパイラ: MSVC
class CBase {
public:
    CBase() = default;
    CBase(int data) : m_data{data} {};
    CBase(const CBase& base) = delete;
    int m_data = 0;
};
int main() {
    CBase base = {1};
}
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+5

Clangを用いて、clang++ -Xclang -ast-dump -fsyntax-only -std=c++14 《C++ファイル》でどのように解釈されるかを確認しました。検証用はコンストラクタの削除等は消して、引数2個のコンストラクタを追加しています。

class CBase
{
      public:
    CBase(int data) : m_data{data} {};
    CBase(int data, int dummy) : m_data{data} {};
    int m_data = 0;
};
int main()
{
    CBase base11 = {1};
    CBase base12{1};
    CBase base13({1});
    CBase base14(1);
    CBase base15 = CBase(1);
    CBase base21 = {1, 2};
    CBase base22{1, 2};
    CBase base23({1, 2});
    CBase base24(1, 2);
    CBase base25 = CBase(1, 2);
}
 初期化                        VerDeclスタイル   コンストラクタ        
 CBase base11 = {1};           cinit             void (int)            
 CBase base12{1};              listinit          void (int)            
 CBase base13({1});            callinit          void (int)            
 CBase base14(1);              callinit          void (int)            
 CBase base15 = CBase(1);      cint              void (class CBase &&) 
 CBase base21 = {1, 2};        cint              void (ini, int)       
 CBase base22{1, 2};           listinit          void (int, int)       
 CBase base23({1, 2});         callinit          void (class CBase &&) 
 CBase base24(1, 2);           callinit          void (int, int)       
 CBase base25 = CBase(1, 2);   cint              void (class CBase &&) 

cintとlistintとcallintは書き方が違うだけで、コンパイルでの処理は全く一緒です。つまり、1)と2)は基本的には一緒で、違いはありません。3)でコンパイルが通るのは、そもそもコピーコンストラクタを使ってないからです。

さて、問題はbase23です。CBase base23({1, 2});と要素が2個になるとムーブコンストラクタに変わってしまいます。一個の時となぜ違うかというと、すいません、わかりませんでした。誰か答えてくれるのを私も待ちます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/07 22:09

    こんな風に初期化の過程観れるんですね。勉強になります。これは恐らく{}の要素が一個の時はint型で解釈されて,複数個ある時は,std::initializer_list<int>型で解釈されるため引数std::initializer_list<int>型のコンストラクタを生成していないからではないでしょうか。

    キャンセル

  • 2016/03/07 23:00 編集

    その方法で確認できるのはプレーンなASTなので、実際にはRVO(copy elide)が有効ならばbase15は1要素コンストラク呼び出しへ、base23とbase25は2要素コンストラクタ呼び出しに最適化されると思います。
    http://melpon.org/wandbox/permlink/kIjNjc4W9kLIIdb9
    (オプション -fno-elide-constructors を外してみてください)

    キャンセル

  • 2016/03/08 22:25 編集

    回答ありがとうございます。

    キャンセル

  • 2016/03/08 22:29

    例えば、CBase base15 = CBase(1); の結果で、ムーブコンストラクタが呼ばれます。
    右辺値はconst CBase&でも保持できるため、コピーコンストラクタとムーブコンストラクタのオーバーロードになると思いますが、ムーブコンストラクタが呼び出されるのは仕様でしょうか?

    キャンセル

  • 2016/03/09 06:44

    ムーブコンストラクタがよばれるのは、`CBase(1)`がrvalueだからです。
    参考: http://qiita.com/you_aizawa/items/8a26c29b8faf9e6172a8
    ただ、yohhoyさんの言うとおり、最適化で普通のコンストラクタになっちゃうようですね。うーん、難しい。

    キャンセル

  • 2016/03/09 20:40

    右辺値はconst&でも受け取れるので、オーバーロードに優先順位が決められていないと、どちらに渡してよいかコンパイラが判断できなくないですか?

    キャンセル

  • 2016/03/09 22:41

    ムーブコンストラクタとコピーコンストラクタ両方が提供されている場合、渡す値がrvalueならばムーブコンストラクタが優先されるそうです。英語ですが、
    http://en.cppreference.com/w/cpp/language/move_constructor
    のNotesの二段落目を参考にして下さい。

    キャンセル

checkベストアンサー

+2

1) 2種類の直接初期化は、全く同じ意味か。

どちらも直接リスト初期化(direct-list-initialization)であり、コンパイラにとっては同義です。

ただし、前者の書き方では警告を引き起こすこともあります。

struct X {
  X(int v) : m{v} {}
  int m;
};

X x1({42});  // warning: braces around scalar initializer [-Wbraced-scalar-init]
X x2{42};    // OK

2) 直接初期化とコピー初期化の違いは何か。

コンストラクタがexplicit修飾されているとき、両初期化の違いがあらわれます。直接初期化はexplicitコンストラクタでも大丈夫ですが、コピー初期化はコンパイルエラーを引き起こします。

struct Y {
  explicit Y(int v) : m{v} {}
  int m;
};

Y y1{42};    // OK
Y y2 = {42}; // error: chosen constructor is explicit in copy-initialization

3) コピーコンストラクタがdeleteなのに、なぜ以下のコードが通るのか。

この書き方での初期化では、コピーコンストラクタを要求せず、普通のコンストラクタを直接呼び出します。ただし2)の通り、explicit修飾なしコンストラクタを要求します。

struct Z1 {
  Z1(int) {}  // 非explicit
  Z1(const Z1&) = delete;
};

struct Z2 {
  Z2(int, int) {}  // 非explicit
  Z2(const Z2&) = delete;
};

Z1 z1 = {1};     // OK
Z2 z2 = {1, 2};  // OK

C++14仕様の§12.6.1 Explicit initialization [class.expl.init]/paragraph 1より引用:

An object of class type can be initialized with a parenthesized expression-list, where the expression-list is construed as an argument list for a constructor that is called to initialize the object. Alternatively, a single assignment-expression can be specified as an initializer using the = form of initialization. Either direct-initialization semantics or copy-initialization semantics apply; see 8.5.

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/11 22:26 編集

    回答ありがとうございます。

    Effective Modern C++の44ページに、x{}とx={}の違いはないと書いてありますが、ユーザー定義型においては、やはりexplicitに関する違いがあると考えて良いということになりますね。

    キャンセル

  • 2016/03/11 23:26

    日本語版では「Effective Modern C++の44ページに、x{}とx={}の違いはないと書いてあり」を見つけられなかったのですが、英語版でしょうか?

    キャンセル

  • 2016/03/12 11:54 編集

    日本語版44ページの一番最初の文です。
    等号と波括弧の併用は、波括弧と同じとあります。

    キャンセル

+1

1.全く同じものです。

2番の項目をraccyさんの結果をみて修正しました。僕の理解も浅かったようで申し訳なかったです。

2.raccyさんの言う通りコンストラクタの引数の指定の仕方に名称がついているだけで,実態は全て同じもののようです。指定した引数に当てはまるコンストラクタの呼び出しを行うという話で,コピーコンストラクタ等の話は関係ありませんでした。(なので=演算子の対象が{}型(void型) ,CBass& 型で呼び出されるコンストラクタが変わってるのは納得できると思います。)

3. 代入している値がCBaseクラス型じゃないからだと思います。検証したい場合はそれぞれのコンストラクターで標準出力で文字列などを出力してみるといいと思います。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/03/08 22:18 編集

    回答ありがとうございます。
    2.に関してです。

    CBase base = CBase(); が、コピーコンストラクタを呼び出すことは分かります。
    CBase base = {}; は、コピーコンストラクタがdeleteでもエラーにならないため(CBase base = CBase() は、コピーコンストラクタがdeleteだとエラー)、CBase base = CBase();とは異なると思いますが、いかがでしょうか。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.47%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C++

    4522questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

  • C++11

    122questions

    C++11は2011年に容認されたC++のISO標準です。以前のC++03に代わるもので、中枢の言語の変更・修正、標準ライブラリの拡張・改善を加えたものです。

  • C++14

    53questions