- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我需要实现对实现相同接口(interface)的对象 vector 的高效访问。直到现在,我一直在使用带有虚函数的继承:接口(interface)被定义为具有纯虚函数的抽象类,每个对象类都实现了虚函数。 vector of objects简单来说就是抽象类上的指针vector(动态访问的例子见文末)。
我需要更快地访问对象集合。因为我在编译时知道所有可能的对象类,所以我使用 boost::variant 来实现对象的集合(即 boost::variant 的 vector )。我需要访问者计划的额外定义来浏览集合。为了明确所有对象都实现相同的接口(interface),我使用 CRTP 来获得静态继承:接口(interface)是 CRTP 抽象,每个对象类都派生自模板化 CRTP 抽象类。
这是一个 CRTP 实现的例子。该接口(interface)简单地定义了两个函数f()
和g(double)
。有两个派生类 C1
和 C2
实现接口(interface)(具有相同的行为)。
#include <vector>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
namespace testVariantSimple
{
// Definition of the interface (abstract class).
template< typename C >
struct CBase
{
void f() { static_cast<C&>(*this).f(); }
int g(const double & x) { return static_cast<C&>(*this).g(x); }
};
// Definition of the first implementation.
struct C1 : public CBase<C1>
{
void f();
int g(const double & x);
};
void C1::f() { return ; }
int C1::g(const double & x) { return sizeof(x); }
// Definition of the second implementation.
struct C2 : public CBase<C2>
{
void f();
int g(const double & x);
};
void C2::f() { return ; }
int C2::g(const double & x) { return sizeof(x); }
// Definition of the visitor for the first function of the interface.
class f_visitor : public boost::static_visitor<int>
{
public:
template< typename C >
int operator()(CBase<C> &c ) const { c.f(); return 0; }
};
// Definition of the visitor for the second function of the interface.
struct g_visitor : public boost::static_visitor<int>
{
const double & x;
g_visitor( const double & x ) : x(x) {}
public:
template< typename C >
int operator()(CBase<C> & c) const { return c.g(x); }
};
// Example of use: construct a random collection and visit it.
void test(int nbSample)
{
typedef boost::variant<C1,C2> CV;
std::vector<CV> vec;
for( int i=0;i<nbSample;++i )
{
switch( std::rand() % 2 )
{
case 1: vec.push_back( C1() ); break;
case 2: vec.push_back( C2() ); break;
}
}
double argdouble;
BOOST_FOREACH(CV & c, vec)
{
boost::apply_visitor( f_visitor(), c );
g_visitor g(argdouble);
boost::apply_visitor( g, c );
}
}
}
这段代码有效,比使用动态继承的代码效率高15倍(使用动态的代码见文末)。对于不熟悉 CRTP 的人来说,代码阅读起来稍微困难一些,但维护或编写起来并不困难。由于 CRTP 的接口(interface)是显式的,访问者的实现相当琐碎,但冗长,难以理解和使用。
我的问题很简单:是否可以从 CRTP 接口(interface)自动定义访问者。我想避免 f_visitor
和 g_visitor
的额外定义,并获得像这样的更具可读性的外观:
BOOST_FOREACH( CV & c, vec )
{
c.f();
c.g(argdouble);
}
感谢您的帮助。对于感兴趣的读者,这里是使用虚拟继承的相同代码。
namespace testDynamicSimple
{
struct CBase
{
virtual void f() = 0;
virtual int g(const double & x) = 0;
};
struct C1 : public CBase
{
void f() {}
int g(const double & x) { return 1; }
};
struct C2 : public CBase
{
void f() {}
int g(const double & x) { return 2; }
};
bool test(int nbSample)
{
typedef boost::shared_ptr<CBase> CV;
std::vector<CV> vec;
for( int i=0;i<nbSample;++i )
{
switch( std::rand() % 5 )
{
case 1: vec.push_back( CV(new C1()) ); break;
case 2: vec.push_back( CV(new C2()) ); break;
}
}
double argdouble = 0.0;
BOOST_FOREACH( CV & c, vec)
{
c->f();
c->g(argdouble);
}
}
}
最佳答案
免责声明:这不是答案,只是可用于解决给定问题的不同方法的基准。
这个想法是将 boost::variant
与其他技术(原始指针上的虚函数、共享指针上的虚函数以及其他一些技术)进行比较。这是我使用的基准代码:
#include <vector>
#include <boost/foreach.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/variant.hpp>
#include <memory>
#include <type_traits>
namespace novirtual {
struct C {
void f() {}
int g(const double &x) { return 1; }
};
void test(int nbSample) {
std::vector<C> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i)
vec.emplace_back(C());
double argdouble = 0.0;
BOOST_FOREACH(C &c, vec) {
c.f();
c.g(argdouble);
}
}
}
namespace crtp {
// Definition of the interface (abstract class).
template <typename C> struct CBase {
void f() { static_cast<C &>(*this).f(); }
int g(const double &x) { return static_cast<C &>(*this).g(x); }
};
// Definition of the first implementation.
struct C1 : public CBase<C1> {
void f();
int g(const double &x);
};
void C1::f() { return; }
int C1::g(const double &x) { return sizeof(x); }
// Definition of the second implementation.
struct C2 : public CBase<C2> {
void f();
int g(const double &x);
};
void C2::f() { return; }
int C2::g(const double &x) { return sizeof(x); }
// Definition of the visitor for the first function of the interface.
class f_visitor : public boost::static_visitor<int> {
public:
template <typename C> int operator()(CBase<C> &c) const {
c.f();
return 0;
}
};
// Definition of the visitor for the second function of the interface.
struct g_visitor : public boost::static_visitor<int> {
const double &x;
g_visitor(const double &x) : x(x) {}
public:
template <typename C> int operator()(CBase<C> &c) const { return c.g(x); }
};
// Example of use: construct a random collection and visit it.
void test(int nbSample) {
typedef boost::variant<C1, C2> CV;
std::vector<CV> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i) {
switch (std::rand() % 2) {
case 0:
vec.push_back(C1());
break;
case 1:
vec.push_back(C2());
break;
}
}
double argdouble;
BOOST_FOREACH(CV & c, vec) {
boost::apply_visitor(f_visitor(), c);
g_visitor g(argdouble);
boost::apply_visitor(g, c);
}
}
}
namespace virt_fun {
struct CBase {
virtual void f() = 0;
virtual int g(const double &x) = 0;
};
struct C1 : public CBase {
void f() {}
int g(const double &x) { return 1; }
};
struct C2 : public CBase {
void f() {}
int g(const double &x) { return 2; }
};
void test(int nbSample) {
std::vector<CBase *> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i) {
switch (std::rand() % 2) {
case 0:
vec.push_back(new C1());
break;
case 1:
vec.push_back(new C2());
break;
}
}
double argdouble = 0.0;
BOOST_FOREACH(CBase * c, vec) {
c->f();
c->g(argdouble);
}
}
}
namespace shared_ptr {
struct CBase {
virtual void f() = 0;
virtual int g(const double &x) = 0;
};
struct C1 : public CBase {
void f() {}
int g(const double &x) { return 1; }
};
struct C2 : public CBase {
void f() {}
int g(const double &x) { return 2; }
};
void test(int nbSample) {
typedef boost::shared_ptr<CBase> CV;
std::vector<CV> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i) {
switch (std::rand() % 2) {
case 0:
vec.push_back(CV(new C1()));
break;
case 1:
vec.push_back(CV(new C2()));
break;
}
}
double argdouble = 0.0;
BOOST_FOREACH(CV & c, vec) {
c->f();
c->g(argdouble);
}
}
}
namespace virt_cont {
struct CBase {
virtual void f() = 0;
virtual int g(const double &x) = 0;
virtual ~CBase() = default;
};
struct C1 final : public CBase {
void f() {}
int g(const double &x) { return 1; }
};
struct C2 final : public CBase {
void f() {}
int g(const double &x) { return 2; }
};
struct foo {
std::aligned_storage<sizeof(C2)>::type buf;
CBase *ptr;
foo(C1 c) { ptr = new ((void *)&buf) C1(c); }
foo(C2 c) { ptr = new ((void *)&buf) C2(c); }
foo(foo &&x) : buf(x.buf) { ptr = reinterpret_cast<CBase *>(&buf); } // UB
foo &operator=(foo &&x) {
buf = x.buf;
return *this;
} // maybe UB?
~foo() { ptr->~CBase(); }
};
void test(int nbSample) {
std::vector<foo> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i) {
switch (std::rand() % 2) {
case 0:
vec.emplace_back(C1());
break;
case 1:
vec.emplace_back(C2());
break;
}
}
double argdouble = 0.0;
BOOST_FOREACH(foo & c, vec) {
c.ptr->f();
c.ptr->g(argdouble);
}
}
}
namespace locals {
struct CBase {
virtual void f() = 0;
virtual int g(const double &x) = 0;
virtual ~CBase() = default;
};
struct C1 final : public CBase {
void f() {}
int g(const double &x) { return 1; }
};
struct C2 final : public CBase {
void f() {}
int g(const double &x) { return 2; }
};
void test(int nbSample) {
C1 c1;
C2 c2;
std::vector<CBase *> vec;
vec.reserve(nbSample);
for (int i = 0; i < nbSample; ++i) {
switch (std::rand() % 2) {
case 0:
vec[i] = &c1;
break;
case 1:
vec[i] = &c2;
break;
}
}
double argdouble = 0.0;
BOOST_FOREACH(CBase * c, vec) {
c->f();
c->g(argdouble);
}
}
}
#include <chrono>
#include <string>
#define VER 4
int main(int argc, char *argv[]) {
int n = 100000;
for (int i = 0; i < 4; ++i) {
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
#if VER == 0
virt_fun::test(n);
#elif VER == 1
shared_ptr::test(n);
#elif VER == 2
crtp::test(n);
#elif VER == 3
virt_cont::test(n);
#elif VER == 4
locals::test(n);
#endif
end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end - start;
std::time_t end_time = std::chrono::system_clock::to_time_t(end);
std::cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
n *= 10;
}
return 0;
}
代码被编译为clang++ -O3 main.cpp -I/Users/aaragon/Local/include
,带有
$ clang++ --version
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
并在配备 2.6 GHz Intel Core i7 处理器和 16 GB 1600 MHz DDR3 内存的 Macbook Pro 上运行。
这些结果考虑了对原始代码的错误修复,以及@dyp 提供的附加代码,该代码使用带有 std::aligned_storage
的包装类。在下表中,no virtual
列对应于根本没有继承,仅供引用。
| size | no virtual| raw ptr | shared_ptr | variant | wrapper | locals |
|-----------|-----------|------------|-----------|-----------|-----------|-----------|
| 100000 | 0.000235s | 0.008309s | 0.030801s | 0.003935s | 0.004222s | 0.001925s |
| 1000000 | 0.002053s | 0.061843s | 0.288403s | 0.029874s | 0.033697s | 0.01478s |
| 10000000 | 0.017687s | 0.627659s | 2.91868s | 0.29699s | 0.322109s | 0.141245s |
| 100000000 | 0.177425s | 6.2493s | 28.9586s | 3.00427s | 3.21402s | 1.40478s |
在这个阶段有一些事情是确定的:boost::shared_ptr
非常慢,并且在 boost::variant
和包装器之间没有明显的赢家。
关于c++ - 自动定义来自 CRTP 的访客(CRTP 使用 boost foreach with boost variant),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24740612/
在complier.h中有一个宏定义如下: # define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0) 但是这里我有一个问题,就是哪里
curl_easy_setopt 的选项在哪里?定义?我试图寻找 CURLOPT_VERBOSE 和其他一些整数值,但这些似乎没有在 curl.h 中明确定义。 最佳答案 第 792 行: #ifde
我确实有一个如下所示的类(class): //.h file class __declspec(dllimport) MyClass { public: //stuff pri
作者: zhuwenzhuang, 2024.05.08. 阅读前假设读者熟悉数据库使用,了解 SQL 的语法和关系算子的大概含义, 能通过 EXPLAIN 命令查看数据库执行计划. 0 前言
我似乎无法找到是否可以声明一个 header 对象以便在响应 header 中重用它,有一些示例定义了响应模式的对象,但它不会转置为响应 header 。我只设法制作了一个可重用的响应对象,如下所示:
css 选择器 * + * 实际上是什么意思?当您执行检查元素时,您可以在谷歌浏览器的控制台中看到它。在我看来,这似乎是对 "Every second child"应用一种风格,但仍然想确定。谁能帮我
我试图弄清楚基本的IO Haskell 函数是定义好的,所以我使用了this reference我到了putChar函数定义: putChar :: Char -> IO () putChar
我得到了一个自动生成的文件,该文件定义了程序集属性,我正在尝试理解内容。 [assembly: global::System.Runtime.Versioning.TargetFrameworkAtt
This文档演示了如何检查变量是否先前已在 gnuplot 脚本中定义。 文档中的示例: a = 10 if (exists("a")) print "a is defined" if (!exist
好吧,这是一个相当基本的问题:我正在关注 SICP 视频,我对 define、let 和 之间的区别有点困惑设置!. 1) 根据 Sussman 在视频中的说法,define 只允许为变量附加一个值一
我一直在尝试定义一个包含只能具有以下三个值之一的字段的 XSD: 绿色 红色 蓝色 本质上,我想在架构级别定义严格的枚举。 我的第一次尝试似乎是错误的,我不确定修复它的“正确”方法。
有人可以定义“POCO”到底是什么意思吗?我越来越频繁地遇到这个术语,我想知道它是否仅与普通类有关还是意味着更多? 最佳答案 “普通旧式 C# 对象” 只是一个普通的类,没有描述基础结构问题或域对象不
在我经常看到的一些django模型中 myfield = models.CharField(_('myfield')) class_name = models.CharField(_('Type'),
每当 BOOL 数据类型不容易预定义时,我都会使用以下定义进行 boolean 运算, typedef unsigned char BOOL; (由于内存使用)。 我意识到出于性能原因,使用本地总线宽
l_ABC_BEANVector = utilRemote.fnGetVector("ABC_COVBEANVector"); 编码的含义是什么?任何帮助,我真的很感激。谢谢 最佳答案 唯一可以肯定地
我正在使用 javacc 开发一个项目,我遇到问题并需要一些帮助,我的文件中有这样的内容: STRING COPYRIGHT (C) 2003, 2004 SYNOPSYS, INC.; 我为单词 S
我想弄清楚基本的 IO定义了 Haskell 函数,所以我使用了 this reference然后我到了 putChar函数定义: putChar :: Char -> IO () putCha
我在具体类中使用 @property 定义 getter 时遇到问题。这是Python代码: from abc import ABCMeta, abstractproperty class abstr
我正在为大学用 C 语言编写一个小游戏,但我陷入了困境。我(在头文件中)有这个结构: typedef struct{ game_element field[MAX_ROWS][MAX_COLU
我一直在 .l 文件中创建标记定义。由于数据集数量庞大,它变得有点乏味。有没有办法读取文件中的所有单词,例如包含所有名词的 noun.txt 并给所有名词一个标记。 基本上,我想自动化这部分: %%
我是一名优秀的程序员,十分优秀!