在this question的上下文中,这是使用back_emplacer
而不是emplace_back
使用std::back_inserter
的方式的C ++ 11 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
以恢复默认的复制/移动分配。谢谢Alex。
答案 0 :(得分:3)
就像复制构造函数一样,复制赋值运算符不是模板(12.8 / 17):
用户声明的副本分配运算符
X::operator=
是类X
的非静态非模板成员函数...
你仍然得到隐式声明的复制赋值和移动赋值运算符,如果使用了odr,它们会被隐式定义,并且它们参与重载解析(所以如果你的参数匹配X const &
或{{ 1}}确切地说,这些将优于模板X &&
)。
答案 1 :(得分:2)
Kerek SB已回答您的第一个问题。第二和第三个问题已在评论和更新问题中得到解答。
但是,使用初始化程序列表和当前版本的迭代器将不起作用。如果您编写类似*I = {1,2,3}
的内容,其中I
的类型为back_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);
}
我添加了第三个赋值运算符,它对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";
}
在这些简单的情况下,如果使用给定的参数构造向量(使用大括号初始化),它几乎与您获得的相同。
我不确定一切是否按预期工作......