恒定时间"缺点"
Constant time 'cons'
有没有办法(例如,修改参数类型(使以下"cons"函数占用恒定时间而不是O(n(时间。 即构建列表应该花费O(n(时间,而不是O(n^2(时间。
我可以在以下情况下这样做吗:
(1( 无动态内存分配
(2( x 必须仍然有效(即不得提及临时(
(3( HEAD 类型可能有一个单独编译的构造函数(不可内联(
#include <type_traits>
template <typename HEAD, typename TAIL>
struct List
{
List(HEAD head, TAIL tail) : head(head), tail(tail) {}
typename std::remove_reference<HEAD>::type head;
typename std::remove_reference<TAIL>::type tail;
};
template <typename HEAD, typename TAIL>
List<HEAD, TAIL> cons(HEAD head, TAIL tail)
{
return List<HEAD, TAIL>(head, tail);
}
struct Empty {};
int main()
{
auto x = cons(1, cons(2, cons(3, Empty())));
// x should still be valid here
}
如何工作的示例。
编译器知道 x 的类型,因此在堆栈上分配空间。
所以堆栈看起来像这样:
| Int1 | Int2 | Int3 |
| p | p+4 | p+8 |
其中p
是任意地址。
编译器创建调用cons(2, cons(3, Empty()))
,将返回值定向到p+4
。
在cons(2, cons(3, Empty()))
内部,编译器创建调用cons(3, Empty())
将返回值定向到p+8
。
这样,每次调用cons
时,都不需要复制tail
。
我只是不确定代码,以便编译器可以(如,允许(进行此优化。如果有另一种获得恒定运行时间的方法,我很乐意使用它。
您似乎正在用一个更糟糕的std::make_tuple
助手重新发明std::tuple
。请改用它。该标准没有为 std::tuple
的转发构造函数或std::make_tuple
提供复杂性保证,但它有点没有意义,因为这两个使用完美的转发,因此每个元素只有一个移动/复制/放置构造用于调用std::make_tuple
;剩下的就是打乱引用。它至少是线性数量的结构。
当然,不能保证您的编译器会优雅地处理所有引用洗牌,但无论如何您都在错误的级别进行优化。
为了便于说明,几乎但不完全是发生的事情:
template<typename... T>
class tuple {
T... members; // this is not correct, only here for illustration
// The forwarding constructor
template<typename... U>
explicit
tuple(U&&... u)
: member(std::forward<U>(u)...)
{}
};
template<typename... T>
tuple<typename std::decay<T>::type...>
make_tuple(T&&... t)
{ return tuple<typename std::decay<T>::type...>(std::forward<T>(t)...); }
因此,在对auto tuple = std::make_tuple(1, 2, 3)
的调用中,从对make_tuple
的调用中有三个临时int
,然后从对make_tuple
内部std::forward<int>(t)...
的第一次调用中有三个int&&
x值,它们绑定到构造函数的参数,这些参数再次作为int&&
x值转发给从它们构造的std::tuple<int, int, int>
的概念上的三个成员。
刚刚意识到我所说的构造数量仅适用于对std::make_tuple
的调用,而不是整个表达式auto tuple = std::make_tuple(...);
。由于元组是从函数返回的,因此可能需要将/RVO移动到最终变量。它仍然是线性的,它仍然是编译器喜欢优化的东西之一,它仍然是担心优化的错误地方。
然而,C++0x充满了善良,它已经可以做到你在答案中描述的事情:
int i = 3;
std::tuple<int, int, int> tuple = std::forward_as_tuple(1, 2, i);
对forward_as_tuple
的调用将返回一个std::tuple<int&&, int&&, int&>
。此帮助程序从不返回带有值的元组,只返回引用的"浅"元组。然后,std::tuple<int, int, int>
的相应转换构造函数将通过两次移动和一个副本初始化其"成员"。请注意,这种愚蠢的(非(优化使您面临编写auto tuple = std::forward_as_tuple(1, 2, i);
的风险,这是一个具有两个悬空引用的元组。
> 在调查之后,我将回答我自己的问题,但如果有人知道更好的方法,我仍然会感兴趣。
(1( 将List
重命名为 TempList
。
(2(使TempList
的构造函数(包括移动和复制(私有。
(3(让cons
成为TempList
的朋友。
(4(将HEAD
和TAIL
成员作为参考。
现在TempList
只保存引用,所以没有副本。但是,这些可能是对临时的引用,但这没关系,因为临时会持续到表达式的末尾,并且由于TempList
只有私有构造函数,因此不能将其分配给表达式的 LHS。只有cons
才能创建TempList
,并且不能超过创建它的表达式。
现在创建一个函数save
或类似效果的东西,它接受一个TempList
并返回一个 实List
,另一种类型,其数据按值存储。
所以我们有
auto x = save(cons(1, cons(2, cons(3, Empty()))));
并且数据最多将被复制或移动一次(通过保存(,整个结构现在正在O(n)
。只要cons
和save
都内联,TempList
结构可能会被优化掉。
- C++为构建时间获取QDateTime的可靠方法
- 从持续时间构造std::chrono::system_clock::time_point
- 我想将一个对T类型的非常量左值引用绑定到一个T类型的临时值
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- while循环中while循环的时间复杂度是多少
- 使用简单类型列表实现的指数编译时间.为什么
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 在基于范围的for循环中使用结构化绑定声明
- 使用 LuaBridge 将 LuaJIT 绑定到C++会导致"PANIC: unprotected error"
- 在已经使用Git的情况下减少编译时间
- 有没有一种方法可以创建一个带有哈希表的数据库,该哈希表具有恒定时间查找功能
- 如何将包含epoch时间的十六进制字符串转换为time_t
- 尝试通过OCI例程从Oracle获取blob数据,但出现错误:ORA-01008:并非所有变量都绑定
- 从文本文件中读取时钟时间和事件时间并进行处理
- C++20 标准::时间::d格式的缺点
- C++ Poco SQL 查询不返回具有'between'和'and'日期时间绑定的结果
- C++17:是编译器为(静态存储持续时间)const引用绑定创建的可修改的临时对象(和存储)
- 模板变量的绑定时间
- 如何判断约束绑定编码的时间限制
- 恒定时间"缺点"