如何生成一个具有唯一值的向量?

7

我有一个例子,可以生成唯一的对象到一个向量中:

#include <iostream>
#include <vector>
#include <algorithm>

int v=0;

struct A
{
    A() : refValue( v++)
    { std::cout<<"constructor refValue="<<refValue<<std::endl; }
    A( const A &r ) : refValue(r.refValue)
    { std::cout<<"copy constructor refValue="<<refValue<<std::endl; }
    A& operator=( const A &r )
    {
        refValue = r.refValue;
        std::cout<<"operator= refValue="<<refValue<<std::endl;
        return *this;
    }
    ~A() { std::cout<<"destructor refValue="<<refValue<<std::endl; }

    int refValue;
};

A GenerateUnique()
{
    A unique;
    return unique;
}
struct B
{
    B( const int n) : v()
    {
        std::generate_n( std::back_inserter( v ), n, &GenerateUnique );
    }
    std::vector< A > v;
};

int main()
{
    B b(3);
}

如果我把我的主函数更改为这个:
struct B
{
    B( const int n) : v(n)
    {
    }
    std::vector< A > v;
};

如果使用上述方式,则会将一个类型为A的对象复制到所有向量元素中。

是否有一种方法可以创建包含所有唯一对象的向量(与第一个示例类似)?

为了更清楚地表达:我有一个包含向量的类。此向量必须包含所有唯一对象(而不是一个对象的副本)。并且我想在初始化列表中初始化它(而不是在构造函数体中初始化)。


我知道我可以使用boost::assign::list_of,但是那需要在编译时知道数量。我需要一个在运行时也能工作的解决方案。 - BЈовић
按照所写的,这真的没有意义。也许你混淆了C++和Java?因为在C++中,std::vector<A> 包含不同的对象,而不是对它们的引用。因此,一个具有N个成员的vector<A>包含N个不同的对象,而不是指向单个对象的N个指针。 - MSalters
我不确定。目前看起来像是 A() != A(),这相当反直觉。你可以将状态移入 GenerateUnique() 中。然而,在这种情况下,你的 std::generate_n() 解决方案实际上是惯用的。 - MSalters
你为什么想要避免构造函数体呢?在我看来,它恰好是用于非平凡初始化的。如果你真的想要,可以将你的第一个 main 转换成一个返回 as 的函数,然后你就可以进行复制初始化,但这样做与在构造函数体中完成相比没有任何优势。 - GManNickG
当然,常见的问题是:为什么?这非常不正规,也许有更好的方法来解决这个问题。 - GManNickG
显示剩余2条评论
3个回答

3

它被复制是因为该构造函数的签名如下:

​explicit vector( size_type count,
             const T& value = T(),
             const Allocator& alloc = Allocator());

很明显,您只是将一个默认构造的对象传递给了这个构造函数并且它进行了复制。
如果您想在初始化列表中进行初始化,很显然您会受到一些对象的构造函数的限制。我猜您不想创建一个包装类来初始化vector,所以我们只能使用vector的构造函数。唯一看起来合理的是:
template <class InputIterator>

vector( InputIterator first, InputIterator last,
        const Allocator& alloc = Allocator() );

因此,您可以创建一个迭代器来返回所需数量的默认构造对象。

我建议在构造函数体中直接进行构造。


也许最后的定义可以与boost函数输入迭代器一起使用? - KillianDS

3
你的首次尝试通常是有效的。 在当前标准C++03中,这行代码:
std::vector< A > as( n ); 

这段内容明确地定义了创建一个A对象并复制n次。我相信在C++0x中,这将改为创建n个默认构造的A(有些微小的区别)。然后,也许你可以在A的构造函数中做些事情来使每个实例都是独一无二的。

目前你不能这样做。


在C++0x中,http://en.cppreference.com/w/cpp/container/vector/vector提到了这一点(第三个构造函数),它说无论如何都会进行复制(这有点争议,因为为什么要创建一个单独的构造函数)。这是不正确的吗?你有C++0x标准关于此的链接吗? - unkulunkulu
@unkulunkulu来自n3290,23.3.6.2向量构造函数、复制和赋值[vector.cons]:3.效果:构造一个具有n个值初始化元素的向量。 - Luc Danton
这里的描述似乎有错误:en.cppreference.com/w/cpp/container/vector/vector - BЈовић
不,那不是错误。第三个构造函数仅在显式请求时使用(不适用隐式转换),而第二个可以利用隐式转换,因此有所区别。 - user283145

1

如已经评论过的那样,您可以使用boost中的make_function_input_iterator,如下所示:

#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/iterator/function_input_iterator.hpp>

// A && GenerateUnique the same ...
struct B
{
    B( const int n) : v(boost::make_function_input_iterator(&GenerateUnique, 1), boost::make_function_input_iterator(&GenerateUnique, n))
    {
    }
    std::vector< A > v;
};

int main()
{
    B b(3);
}

请注意,当我测试代码时,我发现比你的第一个解决方案中有更多的复制构造/赋值操作。除此之外,还创建了一个额外的对象(refvalue 3)(用于最后的“stop”迭代器)。我不知道这种额外的行为是否可行,但如果你真的想要在初始化列表中初始化向量,它确实能起到作用。

正如Bo Persson所说,当前版本的C++(C++03)没有其他方法可以做到这一点。你的版本似乎是唯一的方法。 - BЈовић

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接