如何基于构造函数参数填充一个const成员数组?

问题描述:

比方说,我有这个类:如何基于构造函数参数填充一个const成员数组?

template<int N> 
class A { 
    public: 
    A(const char *s) ... 
    private: 
    const char buf[N]; 
}; 

模板是有这样我就可以不用动态内存分配(的要求)配置阵列尺寸。 buf成员是const,因为它旨在对象初始化后在对象的生命周期内保持不变。

为了澄清,我也没有访问STL。

我有什么选择来定义这个构造函数,以便我可以将s的内容复制到buf?其中一个选项是const_cast,但我正在寻找不需要的替代品。

+2

你可以把'char buf [N]'改成'std :: array'吗? – Jarod42

+0

@ Jarod42不幸的是,我无法访问STL。 – Ana

+0

@Ana你可以复制'libstdC++'的源文件吗?即使你没有访问'std :: array',你现在就可以重新实现它。可能比原来的实施更糟糕。 –

您使用了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"); 

}; 
+2

请注意,您更改签名以采用完全'N'字符。 – Jarod42

+0

@ Jarod42我们可以解决... sec –

+0

@RichardHodges:我已经解决了:P(看我的解决方案) – Nawaz

由@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个字节。

+0

注意:需要C++ 14 –

+0

@ M.M:'std :: make_index_sequence'和'std :: index_sequence'的等价物也可以在C++ 11中实现。 – Nawaz

+1

我喜欢throwable,如果你给它一个constexpr构造函数(和A一样),那么你可以声明变量constexpr - 如果字符串长度错误,这会给你一个编译时异常的堆栈跟踪。 –