2

this questionのコンテキストでは、 how back_emplacerusesemplace_backの代わりに useを使用する C++11 の実装を次に示しstd::back_inserterますpush_back

#include <iterator>
#include <vector>
#include <iostream>

template<class Container>
class back_emplace_iterator : public std::iterator< std::output_iterator_tag,
                                                   void, void, void, void >
{
protected:
    Container* container;
public:
    typedef Container container_type;

    explicit back_emplace_iterator(Container& x) : container(&x) {}

    // ==== FROM UPDATE ====
    template<class T>
    using _not_self =
        typename std::enable_if<
            !std::is_same<
                typename std::decay<T>::type,
                back_emplace_iterator
            >::value
        >::type;
    // =====================

    // ==== UNIVERSAL REFERENCE ASSIGNMENT ====
    template<class T, class = _not_self<T>>
    back_emplace_iterator<Container>&
    operator=(T&& t)
    {
        container->emplace_back(std::forward<T>(t));
        return *this;
    }
    // ========================================

    back_emplace_iterator& operator*() { return *this; }
    back_emplace_iterator& operator++() { return *this; }
    back_emplace_iterator& operator++(int) { return *this; }
};

template< class Container >
inline back_emplace_iterator<Container>
back_emplacer( Container& c )
{
    return back_emplace_iterator<Container>(c);
}

struct Demo
{
    int i;
    Demo(int i) : i(i) {}
};

int main()
{
    std::vector<int> x = {1,2,3,4,5};

    std::vector<Demo> y;

    std::copy(x.begin(), x.end(), back_emplacer(y));

    for (auto d : y)
        std::cout << d.i << std::endl;
}

ユニバーサル参照operator=(T&&)は、デフォルトのコピー代入演算子および/またはデフォルトのムーブ代入演算子の生成を無効にしますか?

もしそうなら、オーバーロードの解決でユニバーサルリファレンスバージョンを打ち負かすように、それらを明示的に定義するにはどうすればよいですか?

そうでない場合、暗黙的に生成されたものはユニバーサルリファレンスバージョンよりも優れていますか?

また、ユニバーサル リファレンス バージョンはイニシャライザ リストで適切に動作しますか?

アップデート:

_not_selfデフォルトのコピー/移動割り当てを復元するためのエイリアス テンプレートが追加されました。ありがとうアレックス。

4

2 に答える 2

2

あなたの最初の質問は、Kerek SB によって回答されました。2 番目と 3 番目の質問は、コメントと質問の更新で回答されています。

ただし、現在のバージョンのイテレータでイニシャライザ リストを使用しても機能しません。*I = {1,2,3}, where Iis of type のようなものを書くとback_emplace_iterator、コンパイラはback_emplace_iteratorブレース初期化を使用して new を構築しようとし (それが正しい表現である場合...)、失敗します。追加するだけoperator=(std::initializer_list<T>)では、すべての場合に機能するとは限りません。T = typename Container::value_type::value_typeコンテナーvalue_typeがそのような初期化子リストから構築可能である場合は、この演算子を使用可能にする方がよいと思います。

これが私が言おうとしていることです:

template<bool Condition>
using EnableIf = typename std::enable_if<Condition>::type;

template<bool Condition>
using DisableIf = typename std::enable_if<!Condition>::type;

struct HasValueTypeImpl
{
    template<class T>
    static auto test(T&&) -> decltype( std::declval<typename T::value_type>(), std::true_type() );
    static auto test(...) -> std::false_type;
};

template<class T>
using HasValueType = decltype( HasValueTypeImpl::test(std::declval<T>()) );

template<class T, class U>
using IsConstructible = typename std::is_constructible<T, U>::type;

template<class Container>
class back_emplace_iterator
    : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
    template<class T>
    using IsSelf = typename std::is_same< typename std::decay<T>::type, back_emplace_iterator >::type;

    Container* container;

public:
    typedef Container container_type;

    explicit back_emplace_iterator(Container& x) : container(&x)
    {
    }

    // 1
    template<
        class T,
        class = DisableIf< IsSelf<T>::value >
    >
    back_emplace_iterator& operator =(T&& t)
    {
        container->emplace_back(std::forward<T>(t));
        return *this;
    }

    // 2
    template<
        class T = typename Container::value_type,
        class = EnableIf<
            HasValueType<T>::value &&
            IsConstructible<T, std::initializer_list<typename T::value_type>>::value
        >
    >
    back_emplace_iterator& operator =(std::initializer_list<typename T::value_type> ilist)
    {
        container->emplace_back(ilist);
        return *this;
    }

    // 3
    back_emplace_iterator& operator =(typename Container::value_type&& t)
    {
        container->emplace_back(std::move(t));
        return *this;
    }

    back_emplace_iterator& operator *() { return *this; }
    back_emplace_iterator& operator ++() { return *this; }
    back_emplace_iterator& operator ++(int) { return *this; }
};

template<class Container>
inline back_emplace_iterator<Container> back_emplacer(Container& c) {
    return back_emplace_iterator<Container>(c);
}

への右辺値参照を取る 3 番目の代入演算子を追加しましたContainer::value_type。これにより、イテレータにさらに多くのものを割り当てることができますが、これは明らかに、この値をインプレースで構築するのではなく、コンテナに移動します。したがって、番号 3 を削除することをお勧めします。

これは簡単なテストケースです。コメントには、使用されている代入演算子と結果のベクトルが記述されています。

int main()
{
    std::vector<std::string> x = {"1","2"};
    std::vector<std::vector<std::string>> vec;

    auto I = back_emplacer(vec);

    *I++ = x;                                           // 1: ["1", "2"]
    *I++ = {x.begin(), x.end()};                        // 3: ["1", "2"]
    *I++ = {5, "xx"};                                   // 3: ["xx", "xx", "xx", "xx", "xx"]
    *I++ = {"eins", "zwei"};                            // 2: ["eins", "zwei"]
    *I++ = {"a", {'b', 'b', 'b'}, std::string("c")};    // 2: ["a", "bbb", "c"]
    *I++ = std::move(x);                                // 3: ["1", "2"]

    std::cout << support::pretty(vec) << "\n";
}

これらの単純なケースでは、指定された引数を使用して (ブレースの初期化を使用して) ベクトルを作成した場合とほぼ同じです。

すべてが期待どおりに機能するかどうかはわかりません...

于 2013-09-11T15:49:11.980 に答える