我有一个固定大小的字符串类,定义如下:
template <typename const std::size_t max_size>
class fixed_string {
...
};
此类保留一个char缓冲区来保存max_size字符。
我希望能够将此类的对象传递给采用模板实例化的方法,该模板实例化具有较低的max_size模板参数值而无需任何复制。例如,以下方法:
void foo(const fixed_string<50>& val) {
}
应该使用fixed_string&lt; 100&gt;来调用,以便以下操作:
void bar() {
fixed_string<100> s = "gg";
foo(s);
}
我怎么能这样做?
答案 0 :(得分:1)
假设编译器以类似的方式将它们排出,那么第一直觉是reinterpret_cast
从大到小fixed_string
,但由于C ++的严格别名规则,这将是非法的(对象可能永远不会被不同类型的引用或指针访问,除非其中一个是[signed|unsigned] char
)
您可以通过创建fixed_string
可以隐式转换为{1}的自定义参考模板来解决此问题:
#include <type_traits>
template<std::size_t N>
struct fixed_string_ref {
char *string;
};
template<std::size_t N>
struct fixed_string_const_ref {
const char *string;
};
template<std::size_t N>
struct fixed_string {
char string[N];
template<std::size_t M, typename std::enable_if<(M < N), int>::type = 0>
operator fixed_string_const_ref<M>() const {
return fixed_string_const_ref<M> { string };
}
template<std::size_t M, typename std::enable_if<(M < N), int>::type = 0>
operator fixed_string_ref<M>() {
return fixed_string_ref<M> { string };
}
};
void foo(fixed_string_const_ref<10>) {}
int main() {
fixed_string<20> f;
foo(f);
//fixed_string<5> g;
//foo(g); <- cannot be converted to fixed_string_const_ref<10>
}
fixed_string[_const]_ref
只保存一个指向fixed_string
数据的指针,因此可以通过值传递而不复制字符串。需要有两个这样的类型,一个是const,一个是非const,以保持const正确性。
enable_if
部分确保只能创建对小于或等于fixed_string
的引用。
可选:通过向fixed_string_const_ref
添加两个构造函数,普通字符串文字也可以作为固定字符串引用传递。
template<std::size_t N>
struct fixed_string_const_ref {
const char *string;
explicit fixed_string_const_ref(const char *s)
: string(s) {}
template<std::size_t M, typename std::enable_if<(M >= N), int>::type = 0>
fixed_string_const_ref(const char (&s)[M])
: string(s) {}
};
// ...
int main() {
// ....
foo("Hello World");
//foo("Hello"); <- cannot be converted to fixed_string_const_ref<10>
}
通过explicit
制作第一个enable_if
和第二个{{1}},只能通过足够长的字符串文字创建引用。
答案 1 :(得分:0)
fixed_string<50>
和fixed_string<100>
是不相关的类型,因此默认情况下不起作用。
一种可能性是fixed_string<N>
继承fixed_string<N-1>
,但这可能会对您的实施造成不必要的限制。
您可以将foo
作为功能模板并使用static_assert
或SFINAE来确保传递正确大小的字符串:
template <std::size_t Size>
void foo (const fixed_string<Size>& val) {
static_assert(Size >= 50, "Size must be at least 50");
}
答案 2 :(得分:0)
首先:模板的两个不同实例是不同的类型,完全不相关。与将float
和char*
提供给完全无法使用的相同方法的问题相同。
因为固定大小的容器类型可以在底层使用相同的方法,所以您可以从基类派生它作为选项。
template <typename const std::size_t max_size>
class fixed_string: public fixed_string_base {...}
然后,这个基类可以保存所有通过虚函数与容器内容交互的方法。
但这似乎是一个设计问题!也许作为一个提示: 如果您在编译时决定容器的大小,但程序的其余部分必须在运行时使用不同类型,则您没有真正的使用好处。也许你的基类有像getSize()这样的方法,它们在每个模板实例中被覆盖,你有与运行时动态数组大小相同的内存/时间成本结果。
答案 3 :(得分:0)
也许您可以在课程中添加复制构造函数,如下所示:
template <int max_size>
struct fixed_string {
template <int sz> fixed_string(const fixed_string<sz> &f) {}
};
然后,您可以将fixed_string<100>
传递给获取const fixed_string<50>&
的功能,但是:
fixed_string<50>
的新实例。一旦不复制数据,这可能并不算太糟糕。因此,在您的类中,您可以拥有静态缓冲区和对缓冲区的引用。默认情况下,引用指向内部缓冲区,但也可以引用其他实例的缓冲区。enable_if
来确保尺寸合适