【发布时间】:2015-05-31 16:19:15
【问题描述】:
简介
我正在开发一个自定义内存分配器,需要在每个分配的块的标题中添加一些簿记信息。有几种不同的块类型,簿记信息也不同。例如,对于线程之间共享的块,需要添加一个引用计数器,对于单个线程使用的块,则不需要添加一个引用计数器。对于从内存池中取出的块,需要保留对原始池的引用,对于从空闲存储中取出的块,则不需要。
问题
所以我想要一个通用接口来为给定的块布局添加和获取某些数据类型。试验这个想法,我得出了一个类似于 std::tuple 的解决方案。然而,与元组不同,我添加到标题中的每种类型都是唯一的。我刚开始学习模板元编程和其他复杂的 c++,但是添加类型的部分对我来说很简单。
我遇到的问题是通过元组的类型模板函数实现类似于 C++14 std::get 的方法。我认为不需要为此编写太多代码,因为编译器能够在方法调用中匹配正确的基类。起初我将get 方法直接放入模板生成的布局类中。但是,在这种情况下,编译器无法匹配正确的类。通过将get 方法移动到另一个手动添加的类层次结构级别,问题得到了解决。
下面的代码演示了这个问题。将 HAVE_GET_IN_LAYOUT 定义为 0 会产生一个有效的解决方案,而将其定义为 1 会产生一个损坏的解决方案 [至少对于 clang++ 3.5 和 3.6]
问题是,在这种情况下发生了什么问题?
#include <cstddef>
#include <iostream>
#ifndef HAVE_GET_IN_LAYOUT
#define HAVE_GET_IN_LAYOUT 0
#endif
constexpr std::size_t Align(std::size_t size, std::size_t offset) {
return (size < 0x8
? (offset + 0x3) & ~0x3
: (size < 0x10 ? (offset + 0x7) & ~0x7 : (offset + 0xf) & ~0xf));
}
template <std::size_t Start, typename... Ts> struct Layout {
static constexpr std::size_t Size = 0;
static constexpr std::size_t Offset = Start;
static constexpr std::size_t TotalSize = Start;
};
template <std::size_t Start, typename T, typename... Ts>
struct Layout<Start, T, Ts...>
: public Layout<Align(sizeof(T), Start) + sizeof(T), Ts...> {
using Type = T;
static constexpr std::size_t Size = sizeof(Type);
static constexpr std::size_t Offset = Align(Size, Start);
static constexpr std::size_t TotalSize = Layout<Offset + Size, Ts...>::TotalSize;
Type value = Offset - Start; // no particular meaning, just for testing.
#if HAVE_GET_IN_LAYOUT
template <typename U, std::size_t X, typename... Us>
U &helper(Layout<X, U, Us...> *c) { return c->value; }
template <typename U> U &get() { return helper<U>(this); }
#endif
};
template <typename... Ts> struct Result : public Layout<0, Ts...> {
#if !HAVE_GET_IN_LAYOUT
template <typename U, std::size_t X, typename... Us>
U &helper(Layout<X, U, Us...> *c) { return c->value; }
template <typename U> U &get() { return helper<U>(this); }
#endif
};
int main() {
std::cout << "layout size <> = " << Layout<0>::TotalSize << std::endl;
std::cout << "layout size <int> = " << Layout<0, int>::TotalSize << std::endl;
std::cout << "layout size <long> = " << Layout<0, long>::TotalSize << std::endl;
std::cout << "layout size <int,int> = " << Layout<0, int, int>::TotalSize << std::endl;
std::cout << "layout size <int,long> = " << Layout<0, int, long>::TotalSize << std::endl;
std::cout << "layout size <long,int> = " << Layout<0, long, int>::TotalSize << std::endl;
std::cout << "layout size <long,long> = " << Layout<0, long, long>::TotalSize << std::endl;
std::cout << "get: " << Result<int, long, long double>{}.get<long>() << std::endl;
return 0;
}
【问题讨论】:
-
两者都在here 工作,结果相同。
-
@Jarod42 虽然不是clang
-
如果我用简单的递归来简化 get/helper 的实现,类似于 @gha.st 所做的 (like so) 也没有问题。
-
A simplified example 也重现了该问题。去掉非类型参数后,clang++ also accepts the program.
标签: c++ c++11 variadic-templates derived-class