web-dev-qa-db-ja.com

ブレース初期化を使用したmake_unique

https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique 次のように書き込みますstd::make_uniqueは次のように実装できます

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

これは、コンストラクターのないプレーンな構造体では機能しません。これらは中括弧で初期化できますが、デフォルト以外のコンストラクターはありません。例:

#include <memory>
struct point { int x, z; };
int main() { std::make_unique<point>(1, 2); }

これをコンパイルする コンパイラは2引数のコンストラクタがないことについて文句を言うでしょう、そして当然そうです。

代わりに中括弧の初期化の観点から関数を定義しない技術的な理由はありますか?のように

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
    return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

それ 十分に機能します 上記のシナリオでは。これが壊れる他の正当なユースケースはありますか?

一般的な傾向が初期化に中括弧を好むように見えることを見ると、そのテンプレートで中括弧を作成することが標準的な選択であると思いますが、標準がそれを行わないという事実は、私が何かを見逃していることを示している可能性があります。

10
MvG

一部のクラスは、2つの初期化スタイルで動作が異なります。例えば.

std::vector<int> v1(1, 2); // 1 element with value 2
std::vector<int> v2{1, 2}; // 2 elements with value 1 & 2

どちらかを選択する十分な理由がない場合があります。標準では1つを選択し、決定を明示的に述べているだけだと思います。

回避策として、独自のmake_uniqueバージョンを実装することをお勧めします。あなたが示したように、それは大変な仕事ではありません。

10
songyuanyao

C++ 20では、これはコンパイルされます:

std::make_unique<point>(1, 2);

新しいルールのため 括弧で囲まれた値のリストから集計を初期化できるようにする


C++ 17では、次のことができます。

std::unique_ptr<point>(new point{1, 2});

ただし、make_sharedでは機能しません。したがって、ファクトリを作成することもできます(演習として左に転送します)。

template <typename... Args>
struct braced_init {
    braced_init(Args... args) : args(args...) { }
    std::Tuple<Args...> args;

    template <typename T>
    operator T() const {
        return std::apply([](Args... args){
            return T{args...};
        }, args);
    }
};

std::make_unique<point>(braced_init(1, 2));

C++ 14では、CTADがまだないため、applyを実装し、braced_initのファクトリ関数を作成する必要がありますが、これらは実行可能です。


一般的な傾向が初期化に中括弧を好むように見える方法を見る

引用が必要です。それは有償のトピックですが、私はその主張に絶対に同意しません。

8
Barry

他の回答に加えて、C++ 17の彼の プレゼンテーション で、Alisdair Meredithmake_uniqueの次の実装を提供します:

template<typename T, typename... Args>
auto make_unique(Args&&... args) -> std::unique_ptr<T> {
    if constexpr (std::is_constructible<T, Args...>::value)
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    else
        return std::unique_ptr<T>(new T{std::forward<Args>(args)...});
}

C + 17 if constexprを使用しますが、それがなくても簡単に書き換えることができます。

このバージョンでは、両方を行うことができます

auto v = make_unique<std::vector<int>>(10, 20); // *v is a vector of 10 elements

そして

auto p = make_unique<point>(10, 20); // *p is a point (10, 20)
1
Evg