web-dev-qa-db-ja.com

make_uniqueおよび完全な転送

標準C++ 11ライブラリにstd::make_unique関数テンプレートがないのはなぜですか?見つける

std::unique_ptr<SomeUserDefinedType> p(new SomeUserDefinedType(1, 2, 3));

少し冗長です。次の方がはるかに優れていませんか?

auto p = std::make_unique<SomeUserDefinedType>(1, 2, 3);

これにより、newが適切に非表示になり、タイプについて1回だけ言及されます。

とにかく、ここに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)...));
}

std::forwardをコンパイルするにはかなり時間がかかりましたが、それが正しいかどうかはわかりません。それは...ですか? std::forward<Args>(args)...はどういう意味ですか?コンパイラはそれで何を作りますか?

214
fredoverflow

C++標準化委員会の議長であるハーブサッターは、彼の ブログ について次のように書いています。

C++ 11にmake_uniqueが含まれていないことは部分的に見落としがあり、ほぼ間違いなく将来追加される予定です。

また、OPで指定されたものと同じ実装を提供します。

Edit:std::make_uniqueC++ 14 の一部になりました。

155
Johan Råde

いいですが、Stephan T. Lavavej(よりよくSTLとして知られています)には、make_uniqueのより良い解決策があります。これは、配列バージョンに対して正しく動作します。

#include <memory>
#include <type_traits>
#include <utility>

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

template <typename T, typename... Args>
std::unique_ptr<T> make_unique_helper(std::true_type, Args&&... args) {
   static_assert(std::extent<T>::value == 0,
       "make_unique<T[N]>() is forbidden, please use make_unique<T[]>().");

   typedef typename std::remove_extent<T>::type U;
   return std::unique_ptr<T>(new U[sizeof...(Args)]{std::forward<Args>(args)...});
}

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

これは 彼のCore C++ 6ビデオ で見ることができます。

Make_uniqueのSTLバージョンの更新バージョンが N3656 として利用可能になりました。このバージョン 採用された ドラフトC++ 14に。

76
tominator

std::make_sharedstd::shared_ptr<Type> ptr(new Type(...));の省略形ではありません。それはあなたができませんなしでやることをします。

その仕事をするために、std::shared_ptrは、実際のポインタ用のストレージを保持することに加えて、追跡ブロックを割り当てる必要があります。ただし、std::make_sharedは実際のオブジェクトを割り当てるため、std::make_sharedはオブジェクトの両方を割り当てますおよび同じメモリブロック内のトラッキングブロック。

したがって、std::shared_ptr<Type> ptr = new Type(...);は2つのメモリ割り当て(1つはnew用、1つはstd::shared_ptrトラッキングブロック用)になりますが、std::make_shared<Type>(...)oneブロックを割り当てますメモリの。

これは、std::shared_ptrの多くの潜在的なユーザーにとって重要です。 std::make_uniqueが行う唯一のことは、もう少し便利です。それ以上のものはありません。

19
Nicol Bolas

C++ 11では、...も(テンプレートコードで) "パック拡張"に使用されます。

要件は、パラメータの展開されていないパックを含む式の接尾辞として使用することです。パックの各要素に式を単純に適用します。

たとえば、あなたの例に基づいて:

std::forward<Args>(args)... -> std::forward<int>(1), std::forward<int>(2),
                                                     std::forward<int>(3)

std::forward<Args...>(args...) -> std::forward<int, int, int>(1,2,3)

後者は間違っていると思います。

また、引数のパックは、展開されていない関数に渡されることはありません。テンプレートパラメータのパックについて確信が持てません。

13
Matthieu M.

Stephan T. Lavavejによる実装に触発されて、配列エクステントをサポートするmake_uniqueがあればいいのではないかと思いました githubにあります そして、それについてコメントをもらいたいです。これを行うことができます:

// create unique_ptr to an array of 100 integers
auto a = make_unique<int[100]>();

// create a unique_ptr to an array of 100 integers and
// set the first three elements to 1,2,3
auto b = make_unique<int[100]>(1,2,3); 
5
Nathan Binkert