223

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

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)...どういう意味ですか? コンパイラはそれから何を作りますか?

4

6 に答える 6

160

C++ 標準化委員会の議長である Herb Sutter は、彼のブログに次のように書いています。

C++11 に含まれていないmake_uniqueのは一部見落としであり、ほぼ確実に将来的に追加されます。

彼はまた、OPによって与えられたものと同じ実装を提供します。

編集: C++14std::make_uniqueの一部になりました。

于 2012-03-11T19:06:52.470 に答える
80

素晴らしいですが、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に採用されました。

于 2012-11-22T12:00:20.767 に答える
19

独自のヘルパーを作成することを妨げるものは何もありませんがmake_shared<T>、ライブラリで提供する主な理由は、実際には とは異なる内部型の共有ポインターを作成し、異なるshared_ptr<T>(new T)方法で割り当てられるためであり、専用のヘルパーなしでこれを達成する方法はないと私は信じていますヘルパー。

make_unique一方、あなたのラッパーはnew式の周りの単なる構文糖衣であるため、目には楽しいように見えるかもしれませんがnew、テーブルには何ももたらしません. 訂正:これは実際には正しくありません: 関数呼び出しでnew式をラップすると、関数を呼び出す場合などに、例外の安全性が提供されますvoid f(std::unique_ptr<A> &&, std::unique_ptr<B> &&)。互いに順序付けされていない2 つの rawnewがあるということは、1 つの新しい式が例外で失敗した場合、もう 1 つの式がリソースをリークする可能性があることを意味します。標準にない理由についてmake_uniqueは、単に忘れられていました。(これは時折発生します。またstd::cbegin、標準にはグローバルが存在するはずですが、グローバルはありません。)

unique_ptrまた、何らかの形で許可する必要がある 2 番目のテンプレート パラメーターを取ることにも注意してください。これはshared_ptr、型の消去を使用して、型の一部にせずにカスタムのデリータを格納する とは異なります。

于 2011-08-12T09:53:06.703 に答える
19

std::make_sharedの省略形だけではありませんstd::shared_ptr<Type> ptr(new Type(...));。それなしではできないことをしてくれます。

ジョブを実行するにはstd::shared_ptr、実際のポインター用のストレージを保持するだけでなく、追跡ブロックを割り当てる必要があります。ただし、std::make_sharedは実際のオブジェクトを割り当てるため、オブジェクトトラッキング ブロックのstd::make_shared両方を同じメモリ ブロックに割り当てることができます。

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

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

于 2011-08-12T09:55:40.290 に答える
13

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)

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

また、引数のパックは展開されていない関数に渡されない場合があります。テンプレート パラメーターのパックについてよくわかりません。

于 2011-08-12T10:23:14.660 に答える
5

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); 
于 2013-01-02T23:22:40.030 に答える