1

auto で値を初期化して、パラメーターとして decltype を期待する関数に渡すことができないのはなぜですか?

シーンを設定して、小さなプログラムをお見せしましょう。


値を返す関数を次に示します。

int Function(void);

この場合、たまたま整数ですが、戻り値の型は変更される可能性があります。
そのため、この次の関数は次のように記述されます。

void What_I_Take_Depends_On_Function(decltype(Function()) x);

関数の戻り値の型を変更する場合、この関数の減速を変更する必要はありません。はい、関数の定義が新しい型を正しく処理しない可能性があります。または、関数の戻り値の型が void に変更された場合、問題が発生しますが、それは私の問題とは関係ありません。

#include <iostream>
#include <cstdlib>

int Function(void){return 5;}

void What_I_Take_Depends_On_Function(decltype(Function()) x){return;}

int main(){

    //assignments(Edit: these are initializations)
    int  var1 = Function();
    auto var2 = Function();

    //initializations
    int  var3 {Function()};
    auto var4 {Function()};

    What_I_Take_Depends_On_Function(var1); //works
    What_I_Take_Depends_On_Function(var2); //works
    What_I_Take_Depends_On_Function(var3); //works
    What_I_Take_Depends_On_Function(var4); //COMPILER ERROR

    //cannot convert ‘std::initializer_list<int>’ to ‘int’ for argument ‘1’ to ‘void What_I_Take_Depends_On_Function(int)’

    return EXIT_SUCCESS;
}

では、なぜ var4 は int ではなく initializer_list なのですか?
Function が int を返すことを auto で推測してから
、減速を var3 に似たものに変更することはできませんか?

4

3 に答える 3

3

ブレース初期化リストは、C++11 の新しい統一初期化構文であり、その変数の型がわかっている場合、{...}自動変数、静的変数、またはメンバー変数を初期化するために使用できます。以下は、有効な C++11 初期化の例です。

class S
{
private:
  int _m;
public:
  S(int m) : _m { m }  // <== initialization of a member
  {}
};


int main() {

  constexpr unsigned i { 10 }; // <== initialization of a constexpr

  S s { i };  // <== initialization by calling constructor

  /* ... */
}

上記のいずれの場合も、はstd::initializer_list生成されません。ブレース初期化リストは単に、引数リストがブレース初期化リスト (§8.5.4) の内容と一致する、指定されたデータ型に適したコンストラクター{...}をコンパイラが識別しようとすることを意味します。

次の 2 つの特殊なケースがあります。

  1. 指定されたデータ型に対して定義されたコンストラクターの 1 つが引数としてを取りstd::initializer_list<T>、brace-init-list の内容がすべて暗黙的に に変換可能である場合T。これは、組み込みのコンテナ タイプの 1 つを使用する場合に当てはまります。

    std::vector<int> vec { 1, 2, 3 };
    

    std::vector<T>にはコンストラクターstd::vector<T>(const std::initializer_list<T>)があるため、brace-init-list は に変換std::initializer_list<int>され、ベクター コンストラクターにコピーされます。コンストラクターはリストを反復処理し、要素を 1 つずつ追加します。

    リストに引数が 1 つしか含まれていない場合、これはちょっとした罠です。

    std::vector<int> vec { 10 };
    

    ここでも同じことが起こるので、1 つの要素を含むベクトルが得られます10。これは、古いスタイルの構文を使用する場合とは異なります。

    std::vector<int> vec(10);
    

    これはstd::vector<int>(const size_t)コンストラクターを呼び出します。つまり、デフォルトで初期化された 10 個の要素のベクトルを作成します。

  2. 初期化する変数の型が事前に決定されていない場合、つまりが宣言で使用される場合:auto

    auto v { 1, 2, 3 };
    

    この場合 (§7.1.6.4/6)、コンパイラは適切なコンストラクターを識別できません。これ、 3 つの整数 (または整数に変換可能な) 引数を取るデータ型が候補になるためです。ここでの規則は、コンパイラがstd::initializer_list<int>のデータ型として想定することですv。それはあなたの場合にも起こることです。

言い換えれば、brace-init-lists の使用は初期化には問題ありません (推奨されます) が、簡単に と組み合わせることはできませんauto。問題を解決するには、データ型を明示的に宣言する必要があります

int var4 { Function() };

または、物事を柔軟に保つために、decltypeここでも使用します。

decltype(Function()) var4 { Function() };

または、古いスタイルの構文を使用できます。

auto v (Function());
于 2012-09-02T01:06:09.003 に答える
2

いいえ、auto「関数が int を返すと推測するだけ」ではありません。auto指定子がどのように機能するかは、7.1.6.4 で明確に定義されており、次のとおりです。

(7.1.6.4/p6) declarator-idの型が 8.3 に従って決定されると、declarator-idを使用して宣言された変数の型は、テンプレート引数推論の規則を使用して初期化子の型から決定されます。変数T識別子に対して決定された型としますd。の出現箇所を新しく考案された型テンプレート パラメータに置き換えるか、イニシャライザが(8.5.4)の場合はに置き換えてPから取得します。変数に対して推定された型は、関数呼び出しからのテンプレート引数推定の規則を使用して決定された推定されます (14.8.2.1)。TautoUbraced-init-liststd::initializer_list<U>dAPは関数テンプレートのパラメーター型であり、の初期化子dは対応する引数です。控除が失敗した場合、宣言は不適切な形式です。

于 2012-09-01T04:03:25.510 に答える
0

構文を誤解していました。
私は次のように変数を初期化することに慣れていました:

int i{0};

ただし、この型を括弧ではなく括弧で初期化する必要があります。
ありがとうレイモンド

于 2012-09-01T05:05:50.090 に答える