14

次のコード行:

bootrec_reset(File(path, size, off), blksize);

プロトタイプを使用した関数の呼び出し:

static void bootrec_reset(File &file, ssize_t blksize);

このエラーが表示されます:

libcpfs / mkfs.cc:99:53:エラー:タイプ「File」の右辺値からのタイプ「File&」の非定数参照の無効な初期化

libcpfs / mkfs.cc:30:13:エラー:「voidbootrec_reset(File&、ssize_t)」の引数1を渡す際に

const &標準に従って、非定数参照()を右辺値に渡すことはできないことを認識しています。ただし、MSVCではこれを行うことができます(この質問を参照)。この質問は理由を説明しようとしますが、彼はリテラルへの参照を使用しているため、答えは意味がありません。リテラルへの参照は、コーナーケースであり、明らかに許可されるべきではありません。

与えられた例では、次の順序のイベントが発生することは明らかです(MSVCの場合と同様)。

  1. Fileのコンストラクタが呼び出されます。
  2. File、、およびへの参照がblksizeスタックにプッシュされます。
  3. bootrec_resetを利用しfileます。
  4. から戻った後bootrec_reset、一時的なFileものは破棄されます。

File非constメソッドが呼び出されるファイルへの一時的なハンドルであるため、参照は非constである必要があることを指摘する必要があります。Fileさらに、そこに構築されるコンストラクター引数を渡したくありません。また、呼び出し元でオブジェクトbootrec_resetを手動で構築して破棄する理由もわかりません。File

だから私の質問は:

  1. この方法で非定数参照を許可しないC++標準を正当化するものは何ですか?
  2. GCCにこのコードを許可させるにはどうすればよいですか?
  3. 今後のC++0x標準はとにかくこれを変更しますか、それとも新しい標準が私に与える何か、ここでより適切なものがありますか?
4

7 に答える 7

12

はい、プレーン関数が非定数参照を一時的なものにバインドできないという事実(しかしメソッドはバインドできます)は常に私を悩ませてきました。TTBOMKの理論的根拠は次のようになります(このcomp.lang.c ++。moderatedスレッドから供給されます):

あなたが持っているとしましょう:

 void inc( long &x ) { ++x; }

 void test() {
     int y = 0;
     inc( y );
     std::cout << y;
 } 

long &xのパラメータをinc()から作成された一時longコピーにバインドすることを許可した場合y、このコードは明らかに期待どおりに機能しません。コンパイラは、y変更されないままのコードをサイレントに生成します。どうやらこれはC++の初期のバグの一般的な原因でした。

C ++を設計した場合、非定数参照を一時的なものにバインドできるようにすることを好みましたが、参照にバインドするときに左辺値から一時的なものへの自動変換を禁止しました。しかし、誰が知っているか、それはワームの別の缶を開いたかもしれません...

于 2010-11-03T05:53:17.977 に答える
7
  • 「この方法で非定数参照を許可しないC++標準を正当化するものは何ですか?」

物事が元々どのように機能したかという反対の慣習での実践的な経験。C ++は、設計された言語ではなく、大部分が進化した言語です。概して、まだ存在しているルールは機能することが判明したものです(ただし、1998年の標準化で発生したいくつかの大きな例外、たとえば、export委員会が既存の慣行を標準化するのではなく発明した悪名高いものなど)。

バインディングルールについては、C ++での経験だけでなく、Fortranなどの他の言語でも同様の経験がありました。

@j_random_hackerが彼の回答で述べているように(私が書いたように、これは0のスコアであり、SOでのスコアは実際には品質の尺度として機能しないことを示しています)、最も深刻な問題は暗黙の変換と過負荷の解決に関係しています。

  • 「GCCにこのコードを許可させるにはどうすればよいですか?」

できません。

それ以外の ...

bootrec_reset(File(path, size, off), blksize);

... 書きます ...

File f(path, size, off);
bootrec_reset(f, blksize);

または、の適切なオーバーロードを定義しますbootrec_reset。または、「巧妙な」コードが魅力的な場合は、原則として、引数参照を適切にconst-castedで返すようにbootrec_reset(tempref(File(path, size, off)), blksize);定義するだけで記述できます。temprefしかし、それは技術的な解決策ですが、そうしないでください。

  • 「今後のC++0x標準はとにかくこれを変更しますか、それとも新しい標準が私に与えるもので、ここでより適切なものがありますか?たとえば、右辺値参照についてのすべての冗談です。」

いいえ、与えられたコードの状況を変えるものは何もありません。

ただし、書き直したい場合は、たとえばC ++ 0x右辺値参照、または上記のC++98回避策を使用できます。

乾杯&hth。、

于 2010-11-03T06:47:31.467 に答える
6

今後のC++0x標準はとにかくこれを変更しますか、それとも新しい標準が私に与える何か、ここでより適切なものがありますか?

はい。すべての名前は左辺値であるため、式を左辺値であるかのように扱うのはほとんど簡単です。

template <typename T>
T& as_lvalue(T&& x)
{
    return x;
}

// ...

bootrec_reset(as_lvalue(File(path, size, off)), blksize);
于 2010-11-03T06:55:34.747 に答える
2
  1. かなり恣意的な決定です-一時的なものがメソッド呼び出しの対象である場合、たとえば、一時的なものへの非定数参照が許可されます(たとえば、ベクトルによって割り当てられたメモリを解放するための「スワップトリック」std::vector<type>().swap(some_vector);) 。
  2. 一時的な名前を付ける以外に、私はあなたがそうすることができないと思います。
  3. 私の知る限り、このルールはC ++ 0xにも存在します(通常の参照の場合)が、rvalue参照は特に存在するため、参照を一時的なものにバインドできます。したがって、bootrec_resetをaに変更File &&すると、コードが有効になります。
于 2010-11-03T04:00:56.637 に答える
1

C ++ 0xを「ジバーリッシュ」と呼んでも、コーディング能力や言語を理解したいという願望をあまりよく表していないことに注意してください。

1)実際にはそれほど恣意的ではありません。非定数参照をr値にバインドできるようにすると、コードが非常に混乱します。私は最近、これに関連するMSVCに対してバグを報告しました。このバグでは、非標準の動作により、標準に準拠したコードがコンパイルに失敗したり、逸脱した動作でコンパイルされたりしました。

あなたの場合、以下を考慮してください:

#include <iostream>

template<typename T>
void func(T& t)
{
    int& r = t;
    ++r;
}

int main(void)
{
    int i = 4;
    long n = 5;
    const int& r = n;

    const int ci = 6;
    const long cn = 7;

    //int& r1 = ci;
    //int& r2 = cn;

    func(i);
    //func(n);

    std::cout << r << std::endl;
}

コメントした行のうち、コンパイルしたいものはどれですか?あなたはfunc(i)その議論を変えたいfunc(n)ですか、そうしませんか?

2)そのコードをコンパイルすることはできません。あなたはそのコードを持ちたくありません。MSVCの将来のバージョンでは、非標準の拡張機能が削除され、そのコードのコンパイルに失敗する可能性があります。代わりに、ローカル変数を使用してください。一時的な場合と同様に、いつでも追加の括弧のペアを使用して、そのローカル変数の存続期間を制御し、コードの次の行の前に破棄することができます。またはr値の参照。

{
  File ftemp(path, size, off);
  bootrec_reset(ftemp, blksize);
}

3)はい、このシナリオではC ++0xr値参照を使用できます。

于 2010-11-03T04:16:39.663 に答える
1

または、単にオーバーロードします。

static void bootrec_reset(File &&file, ssize_t blksize) {
    return bootrec_reset(file, blksize);
}

これが最も簡単な解決策です。

于 2012-10-07T12:24:35.827 に答える
0

GCCにこのコードを許可させるにはどうすればよいですか?

ファイルの定義を所有している場合は、次のようなトリックを試すことができます。

class File /* ... */ {
public:
  File* operator&() { return this; }
/* ... */
};
/* ... */
bootrec_reset(*&File(path, size, off), blksize);

これは、c++98モードでコンパイルされます。

今後のC++0x標準はとにかくこれを変更しますか、それとも新しい標準が私に与える何か、ここでより適切なものがありますか?

明らかに、これは可能な限りの方法です。

于 2015-07-11T11:29:36.767 に答える