私有构造函数禁止使用emplace [_back]()来避免移动

HC4*_*ica 14 c++ access-modifiers move-semantics c++11

请考虑以下代码:

#include <vector>

class A
{
public:    
    A(A&&);  // somewhat expensive

    static std::vector<A> make_As()
    {
        std::vector<A> result;
        result.push_back(A(3));
        result.push_back(A(4));
        return result;
    }

private:
    A(int);  // private constructor
};
Run Code Online (Sandbox Code Playgroud)

因为A移动构造函数有些昂贵(无论出于何种原因),我想避免调用它并使用emplace_back():

#include <vector>

class A
{
public:    
    A(A&&);  // somewhat expensive

    static std::vector<A> make_As()
    {
        std::vector<A> result;
        result.emplace_back(3);
        result.emplace_back(4);
        return result;
    }

private:
    A(int);  // private constructor
};
Run Code Online (Sandbox Code Playgroud)

不幸的是,emplace_back()实际的构造函数调用是由标准库中的某些东西完成的,它没有足够的特权来调用A私有构造函数.

我意识到可能没有什么可以做的,但是我觉得既然调用emplace_back()发生在一个成员中A,他们应该能够调用私有构造函数.

这有什么变通方法吗?

我唯一能想到的是添加一个friend-declaration A,但是需要成为A朋友的精确类(即实际尝试调用构造函数的类)是特定于实现的(例如,for海湾合作委员会(GCC __gnu_cxx::new_allocator<A>)).编辑:刚刚意识到这样的朋友声明会允许任何人emplace_back() A的构建与私有构造成的容器A的,所以它不会真正解决什么,我还不如让构造公众在这一点上...

更新:我应该补充说,A移动构造函数昂贵并不是避免不得不调用它的唯一原因.可能A根本不可移动(也不可复制).vector当然,这不会起作用(因为emplace_back()可能必须重新分配向量),但它会使用deque,它也有类似的emplace_back()方法,但不必重新分配任何东西.

mit*_*ull 17

一种可能的解决方法(或kludge)将使用帮助程序类将参数保存到私有ctor A(让我们调用此类EmplaceHelper).EmplaceHelper也应该有一个私人ctor,它应该是相互友好的A.现在你所需要的只是A中的一个公共ctor,它接受这个EmplaceHelper(通过const-ref),然后使用它emplace_back(EmplaceHelper(...)).

既然EmplaceHelper只能由A你构建,你的公共ctor仍然是有效的私人.

甚至可以用模板化的EmplaceHelper来概括这个想法(可能std::tuple用于保存ctor参数).

编辑:实际上,似乎我过于复杂,因为GManNickG下面的评论给了我一个更简单的想法:EmplaceHelper可能只是一个空类(仍然与私人ctor,与上面的A友好)但它不需要存储所有ctor params ,只需修改您的私有ctor,将EmplaceHelper作为第一个(或最后一个)参数(并公开).效果与上面相同,但没有EmplaceHelper中复杂的逻辑.

像这样:

#include <vector>
class A 
{ 
private:
    struct private_ctor_t {};

public:     
    A(private_ctor_t, int x) : A(x) // C++11 only, delegating constructor
    {}

    A(A&&) { /* somewhat expensive */ }

    static std::vector<A> make_As() 
    { 
        std::vector<A> result; 
        result.emplace_back(private_ctor_t{}, 3); 
        result.emplace_back(private_ctor_t{}, 4); 
        return result; 
    } 

private: 
    A(int) { /* private constructor */ }
}; 
Run Code Online (Sandbox Code Playgroud)

如果委托构造函数是不可用的,你可以分析出每个版本的公共代码,或只是摆脱private_ctor_t干脆只使用新版本,可能与违约第二个参数.

  • 感谢GManNickG进行抛光:)我在地铁上制定了我的答案,在工作的方式,然后无法闪现示例代码;) (2认同)

Nic*_*las 9

根据C++ 11标准,所有标准容器都应该使用该allocator::construct方法进行就地构造.因此,你可以简单地结交std::allocator朋友A.

我认为技术上允许这个函数将实际的构造调用委托给其他东西.就个人而言,我认为规范应该更加严格地强制执行哪些对象调用构造函数以及可以和不可以委派什么.

如果发生此类委托,或出于任何原因,您可以提供自己的分配器,将所有呼叫转发给std::allocator除外construct.我不建议使用后者,因为许多标准容器实现都有特殊的代码来处理,std::allocator这样可以占用更少的空间.

刚才意识到这样的朋友声明会允许任何人使用私有构造函数将emps_back()A构造成A的容器,所以它不会真正解决任何问题,我不妨在那时使构造函数公开......

那么你将不得不决定什么对你更重要:就地建设,或隐藏私人.就其本质而言,就地构造意味着不在您的代码中的人正在进行构建.因此,没有办法解决它:一些外部代码必须被命名为朋友或构造函数必须是公共的.简而言之,构造者必须可以公开访问任何委托构造的人.

  • 我同意这样的观点:鉴于我们在C++中具有的非传递性友谊,应该指定构造构造的方法. (3认同)