2

这个问题的上下文中,这是一个 C++11 的实现,back_emplacer它使用emplace_back而不是如何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

3

就像复制构造函数一样,复制赋值运算符不是模板(12.8/17):

用户声明的复制赋值运算符X::operator=是类的非静态非模板成员函数X...

您仍然可以获得隐式声明的复制赋值和移动赋值运算符,如果使用 odr,它们是隐式定义的,并且它们参与重载解决方案(因此,如果您的参数匹配X const &X &&精确,这些将优先于模板化operator=) .

于 2013-09-10T20:36:42.790 回答
2

Kerek SB 已回答您的第一个问题。第二个和第三个问题已在评论和您的问题更新中得到解答。

但是,将初始化列表与当前版本的迭代器一起使用是行不通的。如果您编写类似*I = {1,2,3}, where Iis of typeback_emplace_iterator的内容,编译器将尝试back_emplace_iterator使用大括号初始化构造一个新的(如果这是正确的措辞...),这将失败。仅添加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);
}

我添加了第三个赋值运算符,它对 a 进行右值引用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 回答