23

与えられた

struct Range{
    Range(double from, double to) : from(from), to(to) {}
    double from;
    double to;
};

struct Box{
    Box(Range x, Range y) : x(x), y(y) {}
    Range x;
    Range y;
};

実行するとしますBox box(Range(0.0,1.0),Range(0.0,2.0))

最適化が有効になっている最新のコンパイラは、Rangeこの構築中にオブジェクトを完全にコピーすることを回避できますか? (つまり、最初にRange内部のオブジェクトを構築しますboxか?)

4

4 に答える 4

29

Rangeコンストラクターに渡される各オブジェクトに対して、実際には 2 つのコピーが実行されます。Range1 つ目は、一時オブジェクトを関数パラメーターにコピーするときに発生します。これは、101010 の回答に記載されている参照に従って省略できます。コピー省略を実行できる特定の状況があります。

2 番目のコピーは、(コンストラクターの初期化リストで指定されているように) 関数パラメーターをメンバーにコピーするときに発生します。これは省略できません。これが、YSC の回答でパラメーターごとに 1 つのコピーが作成されているのをまだ見ている理由です。

コピー コンストラクターに副作用 (YSC の回答の出力など) がある場合でも、最初のコピーに対してコピー省略を実行できますが、2 番目のコピーは残す必要があります。

ただし、コンパイラーは、プログラムの観察された動作を変更しない限り、いつでも自由に変更を加えることができます (これは「as-if」ルールとして知られています)。これは、コピー コンストラクターに副作用がなく、コンストラクター呼び出しを削除しても結果が変わらない場合、コンパイラーは 2 番目のコピーを自由に削除できることを意味します。

これは、生成されたアセンブリを分析することで確認できます。このでは、コンパイラはコピーだけでなく、Boxオブジェクト自体の構築も最適化します。

Box box(Range(a,b),Range(c,d));
std::cout << box.x.from;

以下と同一のアセンブリを生成します。

std::cout << a;
于 2015-11-23T14:23:27.370 に答える
5

すべきですが、うまくいきません(ライブの例)。コンパイラーは、コンストラクターの副作用を検出し、コピー省略を使用しないことを決定する場合があります。

#include <iostream>

struct Range{
    Range(double from, double to) : from(from), to(to) { std::cout << "Range(double,double)" << std::endl; }
    Range(const Range& other) : from(other.from), to(other.to) { std::cout << "Range(const Range&)" << std::endl; }
    double from;
    double to;
};

struct Box{
    Box(Range x, Range y) : x(x), y(y) { std::cout << "Box(Range,Range)" << std::endl; }
    Box(const Box& other) : x(other.x), y(other.y) { std::cout << "Box(const Box&)" << std::endl; }
    Range x;
    Range y;
};


int main(int argc, char** argv)
{
    (void) argv;
    const Box box(Range(argc, 1.0), Range(0.0, 2.0));
    std::cout << box.x.from << std::endl;
    return 0;
}

コンパイルして実行:

clang++ -std=c++14 -O3 -Wall -Wextra -pedantic -Werror -pthread main.cpp && ./a.out

出力:

Range(double,double)
Range(double,double)
Range(const Range&)
Range(const Range&)
Box(Range,Range)
1
于 2015-11-23T13:19:57.830 に答える
4

はい、できます。特に、この種のコピー省略コンテキストは、標準の12.8/p31.3 クラス オブジェクトのコピーと移動 [class.copy]で指定されているコピー省略基準に該当します。

(31.3) -- 参照 (12.2) にバインドされていない一時クラス オブジェクトが同じ型のクラス オブジェクトにコピー/移動される場合 (cv 修飾を無視)、コピー/移動操作は次のように省略できます。省略されたコピー/移動のターゲットに一時オブジェクトを直接構築します。

任意の派生コンパイラは、この特定のコンテキストでコピー省略を適用します。ただし、OP の例では 2 つのコピーが行われます。

  1. コンストラクターに渡された一時オブジェクト (上記のように、標準では省略できます)。
  2. コンストラクターの初期化子リスト内のコピーBox(これは省略できません)。

このデモでは、コピー コンストラクターが 2 回だけ呼び出されていることがわかります。

また、標準では特定のコンテキストでコピー省略の最適化が許可されているため、コンパイラ ベンダーがそれを行う義務があるという意味ではないことにも注意してください。コピー省略は、観察可能な副作用を変更できる最適化の唯一の許可された形式です。したがって、一部のコンパイラは、許可されているすべての状況 (デバッグ モードなど) でコピー省略を実行しないため、コピー/移動コンストラクタおよびデストラクタの副作用に依存するプログラムは移植できません。

于 2015-11-23T13:21:33.530 に答える
1

それができるという事実は、それが最も確実になるという意味ではありません. このDemoを見てください。2 つのコピーを作成していることは明らかです。ヒント、出力には2回含まれています:

コピーした

コピーした

于 2015-11-23T13:30:59.600 に答える