关于变体的问题.我知道Excel vba中的变体既是默认数据类型又效率低(从大型应用程序中过度使用的角度来看).但是,我经常使用它们将数据存储在具有多种数据类型的数组中.我正在研究的一个当前项目本质上是一项需要大量优化非常差的代码(c.7000行)的任务 - 它让我思考; 有没有解决的办法?
解释; 代码经常将数据存储在数组变量中.因此,考虑10列乘10000的数据集.列是多种不同的数据类型(字符串,双精度,整数,日期等).假设我想将它们存储在数组中,我通常会这样做;
dim myDataSet(10,10000) as variant
Run Code Online (Sandbox Code Playgroud)
但是,我的知识说,这将是非常低效的代码评估每个项目,以确定它是什么数据类型(实际上我知道我期待什么).另外,我失去了控制个体数据类型给我的尺寸.所以,(假设前6个是字符串,接下来的4个是为了便于解释这一点),我可以;
dim myDSstrings(6,10000) as string
dim myDSdoubles(4,10000) as double
Run Code Online (Sandbox Code Playgroud)
这让我恢复了控制和效率 - 但也有点笨拙(实际上类型是混合的和不同的 - 我最终在每个中都有奇数个元素,最终必须在代码中单独分配它们 - 而不是质量).所以,它是一个案例;
myDSstrings(1,r) = cells(r,1)
myDSdoubles(2,r) = cells(r,2)
myDSstrings(2,r) = cells(r,3)
myDSstrings(3,r) = cells(r,4)
myDSdoubles(3,r) = cells(r,5)
..etc...
Run Code Online (Sandbox Code Playgroud)
哪个比丑陋得多;
myDataSet(c,r) = cells(r,c)
Run Code Online (Sandbox Code Playgroud)
所以它让我思考 - 我必须在这里遗漏一些东西.存储不同数据类型数组的最佳方法是什么?或者,假设没有办法 - 存储混合数据类型数组的最佳编码实践是什么?
(警告:虽然乍看之下可能看起来像是一个问题,但这不是初学者级别的问题.如果你熟悉"强迫"一词,或者你曾经研究过VBA规范,请继续阅读.)
假设我有一个类型的表达式Variant,我想将它分配给一个变量.听起来很简单吧?
Dim v As Variant
v = SomeMethod() ' SomeMethod has return type Variant
Run Code Online (Sandbox Code Playgroud)
不幸的是,如果SomeMethod返回一个Object(即VarType为vbObject的Variant),则强制启动并v包含该对象的"简单数据值".换句话说,如果SomeMethod返回对TextBox的引用,v则将包含一个字符串.
显然,解决方案是使用Set:
Dim v As Variant
Set v = SomeMethod()
Run Code Online (Sandbox Code Playgroud)
这不幸的是,如果失败,SomeMethod并没有返回一个对象,例如一个字符串,产生类型不匹配错误.
到目前为止,我找到的唯一解决方案是:
Dim v As Variant
If IsObject(SomeMethod()) Then
Set v = SomeMethod()
Else
v = SomeMethod()
End If
Run Code Online (Sandbox Code Playgroud)
这有SomeMethod两次打电话的不幸副作用.
有没有需要调用SomeMethod两次的解决方案?
我的目标是写std::variant,可能没有完全吹,但至少与完全工作的构造函数/析构函数对和std::get<>()函数.
我试图使用char数组保留一个内存.它的大小由最大类型决定,通过使用find_biggest_size<>()函数找到.构造函数使用静态断言,因为它执行检查类型是否在指定类型的列表中.现在,构造函数和就地构造函数工作.
template <typename ... alternatives>
class variant
{
char object[find_biggest_size<alternatives...>::value];
public:
template <typename T>
variant(T&& other)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<T>(other));
}
template <typename T, typename ... ArgTypes>
variant(in_place_t<T>, ArgTypes&& ... args)
{
static_assert(is_present<T, alternatives...>::value, "type is not in range");
new ((T*)&object[0]) T(std::forward<ArgTypes>(args)...);
}
~variant()
{
// what to do here?
}
};
Run Code Online (Sandbox Code Playgroud)
然后我偶然发现了一个问题.我不知道当对象死亡时要执行什么析构函数.最重要的是,不可能访问底层对象,因为我无法专门std::get<>()获得正确的类型.
我的问题是:如何在创建对象后存储类型?这是正确的方法吗?如果没有,我应该使用什么?
编辑:
我试图应用评论.问题是当前存活的类型的索引不能constexpr,因此我无法从类型列表中提取所需的类型并调用适当的析构函数.
~variant()
{
using T = typename …Run Code Online (Sandbox Code Playgroud) 我正在更新当前使用自定义等效std::variant于C++ 17 的代码库.
在代码的某些部分中,变量是从已知的替代方法重置的,因此该类提供了一个断言index()当前值的方法,但仍然无条件地直接调用正确的析构函数.
这用于一些紧密的内环,并且具有(测量的)非平凡的性能影响.这是因为当有问题的替代方案是一个简单的可破坏类型时,它允许编译器消除整个破坏.
从表面上看,在我看来,我无法std::variant<>通过STL中的当前实现实现这一点,但我希望我错了.
有没有办法实现这一点,我没有看到,或者我运气不好?
编辑:根据要求,这是一个用法示例(使用@ TC的示例作为基础):
struct S {
~S();
};
using var = MyVariant<S, int, double>;
void change_int_to_double(var& v){
v.reset_from<1>(0.0);
}
Run Code Online (Sandbox Code Playgroud)
change_int_to_double 有效编译:
@change_int_to_double(MyVariant<S, int, double>&)
mov qword ptr [rdi], 0 // Sets the storage to double(0.0)
mov dword ptr [rdi + 8], 2 // Sets the index to 2
Run Code Online (Sandbox Code Playgroud)
编辑#2
感谢来自@TC的各种见解,我已经登上了这个怪物.即使它跳过一些析构函数确实违反了标准,它也"有效".但是,在编译时检查每个跳过的析构函数都是微不足道的,所以...:
请参阅godbolt:https://godbolt.org/g/2LK2fa
// Let's make sure our std::variant implementation does nothing funky internally.
static_assert(std::is_trivially_destructible<std::variant<char, int>>::value,
"change_from_I …Run Code Online (Sandbox Code Playgroud) 我正在寻找C风格联盟的替代品.boost :: variant就是这样一个选择.std C++中有什么东西吗?
union {
int i;
double d;
}
Run Code Online (Sandbox Code Playgroud) 我std::variant可以是空的(std::monostate),包含一个int,一个std::string或一个bool.
当我想用字符串提供它时,给定为var = "this is my string",它将转换为a bool而不是字符串.如果我明确声明类型,它可以工作var = std::string("this is my string").为什么会这样,我能做些什么来避免它?
#include <string>
#include <variant>
#include <iostream>
int main()
{
using var = std::variant<std::monostate, int, std::string, bool>;
var contains_nothing;
var contains_int = 5;
var contains_string = "hello";
var contains_expl_string = std::string("explicit hello");
var contains_bool = false;
auto visitor = [](auto&& arg){
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same<T, std::monostate>())
std::cout<<"nothing\n";
else if constexpr …Run Code Online (Sandbox Code Playgroud) 的方式std::variant分派给不同的访问者方法时std::visit被称为是非常合理的,当变异的替代品是完全不同的类型.本质上,特定vtable于访问者的特定是在编译时构建的,并且在一些错误检查1之后,通过基于当前索引表来查看适当的访问者函数,该当前index()解析为大多数平台上的间接跳转.
但是,如果备选方案共享一个公共基类,则调用(非虚拟)成员函数或使用访问者访问基类上的状态在概念上要简单得多:您总是调用相同的方法并且通常使用相同的指针2来基类.
尽管如此,实施结果同样缓慢.例如:
#include <variant>
struct Base {
int m_base;
int getBaseMember() { return m_base; }
};
struct Foo : public Base {
int m_foo;
};
struct Bar : public Base {
int m_bar;
};
using Foobar = std::variant<Foo,Bar>;
int getBaseMemVariant(Foobar& v) {
return std::visit([](auto&& e){ return e.getBaseMember(); }, v);
}
Run Code Online (Sandbox Code Playgroud)
为最新版本的在x86生成的代码gcc和clang类似3(示出铛):
getBaseMemVariant(std::__1::variant<Foo, Bar>&): # @getBaseMemVariant(std::__1::variant<Foo, Bar>&)
sub …Run Code Online (Sandbox Code Playgroud) I recently followed a Reddit discussion which lead to a nice comparison of std::visit optimization across compilers. I noticed the following: https://godbolt.org/z/D2Q5ED
Both GCC9 and Clang9 (I guess they share the same stdlib) do not generate code for checking and throwing a valueless exception when all types meet some conditions. This leads to way better codegen, hence I raised an issue with the MSVC STL and was presented with this code:
template <class T>
struct valueless_hack {
struct tag {}; …Run Code Online (Sandbox Code Playgroud) Dim Result() As Variant
Run Code Online (Sandbox Code Playgroud)
在我的观察窗口中,显示为
Expression | Value | Type
Result | | Variant/Variant()
Run Code Online (Sandbox Code Playgroud)
我如何检查以下内容:
if Result is nothing then
Run Code Online (Sandbox Code Playgroud)
要么
if Result is Not Set then
Run Code Online (Sandbox Code Playgroud)
这基本上是我想要完成的,但第一个不起作用,第二个不存在.