如何基于构造函数参数填充一个const成员数组?
比方说,我有这个类:如何基于构造函数参数填充一个const成员数组?
template<int N>
class A {
public:
A(const char *s) ...
private:
const char buf[N];
};
模板是有这样我就可以不用动态内存分配(的要求)配置阵列尺寸。 buf
成员是const
,因为它旨在对象初始化后在对象的生命周期内保持不变。
为了澄清,我也没有访问STL。
我有什么选择来定义这个构造函数,以便我可以将s
的内容复制到buf
?其中一个选项是const_cast
,但我正在寻找不需要的替代品。
您使用了index_sequence和模板扩展。
#include <utility>
#include <cstdlib>
template<int N>
class A {
template<size_t...Is>
A(const char (&s)[N], std::index_sequence<Is...>)
: buf{ s[Is]... }
{}
public:
A(const char (&s)[N])
: A(s, std::make_index_sequence<N>())
{
}
private:
const char buf[N];
};
int main()
{
A<3> a("ab");
};
而且由于为const char []是一个文本类型,它也允许类是constexpr:
#include <utility>
#include <cstdlib>
template<int N>
class A {
template<size_t...Is>
constexpr A(const char (&s)[N], std::index_sequence<Is...>)
: buf{ s[Is]... }
{}
public:
constexpr A(const char (&s)[N])
: A(s, std::make_index_sequence<N>())
{
}
private:
const char buf[N];
};
int main()
{
constexpr A<3> a("ab");
};
但改变了签名......
那么,这个:
#include <utility>
#include <cstdlib>
#include <cstring>
template<int N>
class A {
template<size_t...Is>
constexpr A(const char *s, size_t len, std::index_sequence<Is...>)
: buf{ (Is <= len ? s[Is] : char(0))... }
{}
public:
constexpr A(const char *s)
: A(s, strlen(s), std::make_index_sequence<N>())
{
}
private:
const char buf[N];
};
int main()
{
constexpr A<10> a("ab");
};
由@Richard Hodges提供的解决方案要求用char数组初始化类,而不是char const*
,其中更改构造函数的签名。如果没有你的情况下工作,那么这里是一个解决方案,它不不更改签名:
template<int N>
class A
{
template<size_t...Is>
A(const char * s, std::index_sequence<Is...>)
: _assert(std::strlen(s) <= N, "size of buffer is bigger than N"),
buf{ (Is, *s != '\0'? *s++: *s)... }
{}
public:
A(const char *s) : A(s, std::make_index_sequence<N>()) {}
private:
throwable _assert;
const char buf[N];
};
其中throwable
被定义为:
struct throwable
{
throwable(bool b, char const * message){
if (not b)
throw std::invalid_argument(message);
}
};
采用throwable
保证buf
未使用大于N
字节的缓冲区进行初始化。但是,如果您的情况确保并因此不需要此检查,您可以将其删除。该代码应该工作没有它,但我建议你保持它至少在调试模式。
请注意,增加_assert
作为成员会将该类的大小增加至少一个字节。为了避免这种情况,我们可以使用empty base class optimiation做这个:
template<int N>
class A : private throwable
{
template<size_t...Is>
A(const char * s, std::index_sequence<Is...>)
: throwable(std::strlen(s) <= N, "size of buffer is bigger than N"),
buf{ (Is, *s != '\0'? *s++: *s)... }
{}
public:
A(const char *s) : A(s, std::make_index_sequence<N>()) {}
private:
const char buf[N];
};
这为我们节省了每个实例1个字节。
注意:需要C++ 14 –
@ M.M:'std :: make_index_sequence'和'std :: index_sequence'的等价物也可以在C++ 11中实现。 – Nawaz
我喜欢throwable,如果你给它一个constexpr构造函数(和A一样),那么你可以声明变量constexpr - 如果字符串长度错误,这会给你一个编译时异常的堆栈跟踪。 –
你可以把'char buf [N]'改成'std :: array'吗? – Jarod42
@ Jarod42不幸的是,我无法访问STL。 – Ana
@Ana你可以复制'libstdC++'的源文件吗?即使你没有访问'std :: array',你现在就可以重新实现它。可能比原来的实施更糟糕。 –