放置复杂物体
Emplace for complex objects
我正在尝试在对象中实现 "emplace" 功能。它的结构如下。我有一个模板对象,将 size_t 与模板类型配对。我希望能够在标准库容器(例如向量)中就地构建它。我没有使用 std::pair
,因为我的 class 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");
}
每个参数允许一个用户定义的转换序列,并且您有两个:
const char*
--> std::string
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
这不起作用的原因不是因为您不能进行隐式转换。在以下代码中:
vector<B<C>> v;
v.emplace_back(5, "Hello");
emplace_back
完美转发到 v
包含的类型的构造函数,即 B<C>
。所以我们最终调用了(它实际上是通过分配器发生的,但让我们忽略细节):
B<C>(5, "Hello")
为什么这行不通?你经常听到人们说调用模板时无法进行隐式转换;这是大致正确的。但是 B<C>
的构造函数不是模板; B
是一个模板 class ,它已经被模板化了,所以 B<C>
是一个普通的 class 并且它的构造函数不是模板。所以隐式转换 是 允许的。
但是,C++ 中的隐式转换包括:
- 零个或一个标准转换序列;
- 零个或一个用户定义的转换;
- 零个或一个标准转换序列。
(http://en.cppreference.com/w/cpp/language/implicit_conversion).
用户定义的转换基本上是 class 定义隐式构造函数或转换运算符。 string
可能是标准库的一部分,但它只是一个普通的 class,从 const char*
到 string
的转换已经算作用户转换了。所以你不能得到第二个从 string
到 C.
解决此问题的一种方法是为 C 构建构造函数模板,但单参数隐式模板构造函数会使您面临各种其他问题。我可能只定义第二个构造函数,它采用 const char*
。在 cppcon 上就这个问题进行了一次完整的讨论,顺便说一句:https://www.youtube.com/watch?v=PNRju6_yn3o.
我正在尝试在对象中实现 "emplace" 功能。它的结构如下。我有一个模板对象,将 size_t 与模板类型配对。我希望能够在标准库容器(例如向量)中就地构建它。我没有使用 std::pair
,因为我的 class 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");
}
每个参数允许一个用户定义的转换序列,并且您有两个:
const char*
-->std::string
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
这不起作用的原因不是因为您不能进行隐式转换。在以下代码中:
vector<B<C>> v;
v.emplace_back(5, "Hello");
emplace_back
完美转发到 v
包含的类型的构造函数,即 B<C>
。所以我们最终调用了(它实际上是通过分配器发生的,但让我们忽略细节):
B<C>(5, "Hello")
为什么这行不通?你经常听到人们说调用模板时无法进行隐式转换;这是大致正确的。但是 B<C>
的构造函数不是模板; B
是一个模板 class ,它已经被模板化了,所以 B<C>
是一个普通的 class 并且它的构造函数不是模板。所以隐式转换 是 允许的。
但是,C++ 中的隐式转换包括:
- 零个或一个标准转换序列;
- 零个或一个用户定义的转换;
- 零个或一个标准转换序列。
(http://en.cppreference.com/w/cpp/language/implicit_conversion).
用户定义的转换基本上是 class 定义隐式构造函数或转换运算符。 string
可能是标准库的一部分,但它只是一个普通的 class,从 const char*
到 string
的转换已经算作用户转换了。所以你不能得到第二个从 string
到 C.
解决此问题的一种方法是为 C 构建构造函数模板,但单参数隐式模板构造函数会使您面临各种其他问题。我可能只定义第二个构造函数,它采用 const char*
。在 cppcon 上就这个问题进行了一次完整的讨论,顺便说一句:https://www.youtube.com/watch?v=PNRju6_yn3o.