自动 x = make_x(..) 和这个
auto x = make_x(...) and this
我有一个类模板S<T>
,因为模板参数有时很难明确编写,所以我还有一个小的辅助函数makeS(...)
来推断模板参数。
现在的问题是S
的构造函数有一个"副作用":它将自己添加到一个map
,然后稍后将用于迭代所有S
实例。但这实际上使S<T> s{...};
与auto s = makeS(...);
非常不同(如果不使用 RVO)。
这里有一些代码,希望能显示我正在尝试做的事情。 (注意:在实际程序中,S
有多个模板参数,所有参数都将在makeS
中推导出来)
#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <utility>
using namespace std;
struct Base
{
virtual ~Base() {}
virtual void f() const = 0;
};
static map<string, Base*> Map;
template <typename T>
struct S : Base
{
T Func;
Base* This;
S(string const& name, T func) : Func(std::move(func)), This(this)
{
//
// Automatically add this struct to Map...
//
Map.insert({name, this});
}
virtual void f() const override { Func(); }
};
template <typename T>
S<T> makeS(std::string const& name, T func)
{
return S<T>(name, std::move(func));
}
void func1()
{
std::cout << "func1n";
}
int main()
{
struct Func2
{
void operator ()() const {
std::cout << "func2n";
}
};
//
// This is not possible:
//
// S< ??? > s("s", [](){});
//
// This would be ok:
//
// auto F = [](){};
// S<decltype(F)> s("s", F);
//
auto s1 = makeS("s1", func1);
auto s2 = makeS("s2", Func2());
auto s3 = makeS("s3", [](){ std::cout << "func3n"; });
//
// Manually adding s1,... to the Map is ok, but that's what
// I want to avoid...
//
// Map.insert({"s1", &s1});
// ...
//
assert(&s1 == s1.This);
assert(&s2 == s2.This);
assert(&s3 == s3.This);
for (auto&& I : Map)
{
I.second->f();
}
}
据我了解,只有在auto s1 = makeS(...)
等中使用 RVO 时,地图才会包含有效的指针,这不能保证。
有没有办法推断模板参数,同时避免手动注册s1,...
?
你的基本问题是你未能实现 3 规则。 如果你的析构函数需要非平凡的行为(如果你在构造函数中注册自己,就是这种情况),你必须实现或阻止赋值和复制构造(和/或移动赋值和移动构造)。
在这种情况下,我们可以实现一个move
-construct和blockmove
-assign,并且复制构造和复制分配被隐式阻止。
首先,将name
添加到S
。 然后实现move
构造函数。
template <typename T>
struct S : Base
{
std::string Name;
T Func;
Base* This; // ?? why ?? this looks both dangerous and useless at the same time!
S( S&& s ): Name(std::move(s.Name)), Func(std::move(s.Func)), This(this) {
s.clear(); // technically `move` need not clear.
map[Name] = this; // overwrite
}
S& operator=(S&& s) = delete; // or implement it
现在您的对象move
,当move
d 时,它会更新Map
。~S
,我假设,从Map
取消注册 - 检测你的name
是否为空(并在构造时断言你获得一个非空名称),如果是,不要取消注册(因为你已经从move
d)。
现在move
构造和省略构造具有相同的语义。 RVO 故障会导致一些效率低下,但没有逻辑故障。 另外,您的类型现在move
能力,这往往非常有用。
如果需要维护对象标识,使用 可以使用std::unique_ptr
:
template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
return { new S<T>(name, std::move(func)) };
}
现在,将指针从一个位置移动到另一个位置不会移动对象;保留在地图中的指针将保持有效。
我对改进代码的建议:
1)摆脱构造函数中的副作用。仅在工厂方法中创建对象(makeS
在代码中;您可以使其成为S
的静态成员函数),并在该方法中注册S
对象。要以不同的方式禁用对象创建,请将构造函数设为私有。
2)禁用S
对象复制/移动,并将对象处理为例如shared_ptr
/unique_ptr<S>
。禁用复制/移动时,可以避免地图包含指向无效对象的指针时出现问题,现在您不需要依赖 RVO。
3)使用std::function<void()>
代替T Func;
。在这种情况下,您的类不需要是模板类(或者它将具有较少的模板参数)。这将简化您的代码。
- 'short int'持有的值溢出,但"自动"不会溢出?
- 普通环路未使用gcc 4.8.5自动矢量化
- 为什么这个 c++ 代码打印出长度 5,当我打印出字符串时,程序会自动终止?
- VS Code "command":"make"与终端窗口中的命令行"make"不同
- QwtPlot具有相等的轴和自动缩放
- 缓存std::数组的选定元素,并在c++中自动保持其一致性
- Visual Studio 2017循环自动向量化问题
- 当C++中需要自动删除时,这是静态的正确用法吗?
- 自动重复 extern "C"函数中的类构造函数采用的相同参数
- 我怎样才能将所有子目录与 cmake 自动匹配
- GCC 4.8.2 自动矢量化由于 cout 而失败
- 自动"yes"到 Popen SSH 命令
- 有没有办法通过使用十进制 ASCII 代码自动类型扣除来获取字符?
- 自动格式化程序> >更改为>>
- 为什么"make distcheck"在这个非常基本的"Hello, World!"自动工具示例中失败?
- 尝试自动生成依赖项时"make: *** No targets. Stop."
- 自动制作库工具'No rule to make target'
- 用make和自动依赖关系构建程序
- (make/g++)包含自动生成的依赖目标的完整路径?(或方法)
- 当环境变量发生变化时,让基于makefile的cmake项目自动运行make-rebuild_cache