【问题标题】:Emplace for complex objects放置复杂对象
【发布时间】:2017-12-18 20:35:50
【问题描述】:

我正在尝试在对象中实现“安放”功能。它的结构如下。我有一个将 size_t 与模板类型配对的模板对象。我希望能够在标准库容器中构建它,例如向量。我没有使用std::pair,因为我的班级B 将围绕数据提供其他功能。

我需要如何修改代码,以便可以像在 main 中一样调用 emplace?

#include <iostream>                                                                 
#include <vector>                                                                   
using namespace std;                                                                

class C {                                                                           
  public:                                                                           
    C(const string& _s) : s(_s) {}                                                  
    C(string&& _s) : s(_s) {}                                                       

  private:                                                                          
    string s;                                                                       
};                                                                                  

template<typename A>                                                                
class B {                                                                           
  public:                                                                           

    B(size_t _i, const A& _a) : i(_i), a(_a) {}                                     
    B(size_t _i, A&& _a) : i(_i), a(_a) {}                                                  

  private:                                                                          
    size_t i;                                                                       
    A a;                                                                            
};                                                                                  

int main() {                                                                        
  vector<B<C>> v;                                                                   
  v.emplace_back(5, "Hello");                                                       
}

【问题讨论】:

  • v.emplace_back(5, C("Hello"))
  • @0x499602D2 这不会在向量中调用额外的 C 副本吗?我希望避免这种情况,并为我的班级提供漂亮的语法
  • 字符串"Hello" 需要转换为C。如果不创建一个临时的 C("Hello") 对象,这是不可能发生的。不会发生副本,因为它绑定到右值引用_a。实际复制发生在a(_a),即使没有制作临时副本也会发生。
  • @0x499602D2 啊,我明白了。至于追求所需的语法,有什么可能的解决方法吗?
  • v.emplace_back(5, std::string{"Hello"}) ? (也许在移动构造函数中有一些std::move()?)

标签: c++ templates c++14 emplace


【解决方案1】:

每个参数允许一个用户定义的转换序列,并且您有两个:

  1. const char* --> std::string
  2. std::string --> C

你可以做的是通过实现一个通用的转发构造函数来减少隐式转换的数量:

template<typename A>                                                                
class B {                                                                           
  public:

    template<typename X>
    B(size_t _i, X&& _a) : i(_i), a(std::forward<X>(_a)) {}                                                  

  ...
};

这将生成一个构造函数B(size_t, const char*),它将完美匹配emplace_back(5, "Hello"),只需一次转换即可从const char *构造a。

【讨论】:

    【解决方案2】:

    这不起作用的原因不是因为您不能进行隐式转换。在以下代码中:

    vector<B<C>> v;                                                                   
    v.emplace_back(5, "Hello");
    

    emplace_back 完美转发到v 包含的类型的构造函数,即B&lt;C&gt;。所以我们最终会调用到(它实际上是通过分配器发生的,但让我们忽略细节):

    B<C>(5, "Hello")
    

    为什么这不起作用?你经常听到有人说调用模板时不能得到隐式转换;这大致上是真的。但是B&lt;C&gt;的构造函数不是模板; B 是一个模板类,已经被模板化了,所以B&lt;C&gt; 是一个普通类,它的构造函数不是模板。所以允许隐式转换。

    但是,C++ 中的隐式转换包括:

    1. 零个或一个标准转换序列;
    2. 零次或一次用户定义的转换;
    3. 零个或一个标准转换序列。

    (http://en.cppreference.com/w/cpp/language/implicit_conversion).

    用户定义的转换基本上是一个定义隐式构造函数或转换运算符的类。 string 可能是标准库的一部分,但它只是一个普通的类,从const char* 到string 的转换已经算作用户转换。所以你不能从string 得到第二个到 C。

    解决此问题的一种方法是将您的构造函数模板化为 C,但单参数隐式模板构造函数会使您面临各种其他问题。我可能只是定义了第二个构造函数,它采用const char*。在 cppcon,顺便说一句:https://www.youtube.com/watch?v=PNRju6_yn3o。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-06
      • 1970-01-01
      • 1970-01-01
      • 2019-09-20
      • 2014-09-25
      • 1970-01-01
      相关资源
      最近更新 更多