C++ 在编译时确定参数的唯一性 问题
我有以下类似数组的容器:C++ 在编译时确定参数的唯一性 问题,c++,templates,c++14,variadic-templates,template-meta-programming,C++,Templates,C++14,Variadic Templates,Template Meta Programming,我有以下类似数组的容器: template < class _Tp, size_t _Size > class Alphabet { public: template <class... _Ts> constexpr Alphabet(_Ts&& ... ts) : m_data(std::forward<_Ts>(ts)...}) {} private: _Tp m_d
template <
class _Tp,
size_t _Size
> class Alphabet {
public:
template <class... _Ts>
constexpr
Alphabet(_Ts&& ... ts)
: m_data(std::forward<_Ts>(ts)...})
{}
private:
_Tp m_data[_Size ? _Size : 1];
}
我使用C++14。感谢您的帮助 如果要检查编译时(
static\u assert()
)的ts..
值,则必须将它们作为编译时已知的值传递
不能在编译时检查constexpr
构造函数的参数,因为构造函数也可以与运行时值一起使用
我的建议是将ts..
值作为模板参数传递,因此它们是已知的编译时,因此您可以在静态断言()测试中检查它们
举例:如果Tp
是整数类型,则可以将它们作为std::integer\u序列的参数传递
template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }
关于是唯一的()
,您可以使用未使用的数组初始化的技巧
template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];
bool ret = true;
(void)unused{ false, ret &= T0 != Ts... };
return ret;
}
并按如下方式使用它
constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
constexpr auto{make_Alphabet()};
下面是一个完整的C++14编译示例
#include <utility>
#include <iostream>
template <typename Tp, std::size_t Size>
class Alphabet
{
private:
Tp m_data[Size ? Size : 1U];
template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];
bool ret = true;
(void)unused{ false, ret &= T0 != Ts... };
return ret;
}
// ground case
template <typename = void>
static constexpr bool are_unique ()
{ return true; }
// recursive case
template <Tp T0, Tp ... Ts>
static constexpr bool are_unique ()
{ return is_unique<T0, Ts...>() && are_unique<Ts...>(); }
public:
template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }
};
template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
{ return { std::integer_sequence<Tp, Ts...>{} }; }
int main()
{
// compilation error (static_assert failed "!")
//constexpr Alphabet<char, 3>
// aba{std::integer_sequence<char, 'a', 'b', 'a'>{}};
// compile
constexpr Alphabet<char, 3>
abc{std::integer_sequence<char, 'a', 'b', 'c'>{}};
// compilation error (static_assert failed "!")
//constexpr auto abca{ make_Alphabet<char, 'a', 'b', 'c', 'a'>() };
// compile
constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
}
#包括
#包括
模板
类字母表
{
私人:
Tp m_数据[大小?大小:1U];
模板
静态constexpr bool是唯一的()
{
使用unused=bool[];
bool-ret=真;
(void)未使用的{false,ret&=T0!=Ts…};
返回ret;
}
//案情
模板
静态constexpr bool是唯一的()
{返回true;}
//递归案例
模板
静态constexpr bool是唯一的()
{return is_unique()&&are_unique();}
公众:
模板
constepr字母表(std::integer_sequence const&)
:m_数据{Ts..}
{static_assert(sizeof…(Ts)参数参数值永远不是constexpr,您可以使用std::integral_constant
参数。或者通过template class Alphabet;
更改您的类。这并不能解决问题,但名称以下划线开头,后跟大写字母(\u Tp
,\u Size
,\u Ts
)包含两个连续下划线的名称保留供实现使用。不要在代码中使用它们。常规函数和构造函数参数永远不会是constexpr
,因为可以使用运行时值调用它们。如果要强制在编译时执行此操作,则需要使用模板;如果要使用不是内置类型或指针的类型,这在C++20之前是不可能的,因为非类型模板参数只允许某些类型。如果您只想允许它在编译时发生(如果值可用),那么可以使用constepr
函数进行检查,如果出现问题,则引发异常(如果在编译时进行计算,这将成为一个错误)。@DanielH我也这么想。不幸的是,我的印象是我可以将参数设置为constexpr
。不过,如果编译器允许,我会按照你说的做,并尝试在编译时对字母表进行计算。
error: non-constant condition for static assertion
static_assert(detail::is_unique(ts...),
^
error: 'ts#0' is not a constant expression
template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }
// ground case
template <typename = void>
static constexpr bool are_unique ()
{ return true; }
// recursive case
template <Tp T0, Tp ... Ts>
static constexpr bool are_unique ()
{ return is_unique<T0, Ts...>() && are_unique<Ts...>(); }
template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];
bool ret = true;
(void)unused{ false, ret &= T0 != Ts... };
return ret;
}
template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
{ return { std::integer_sequence<Tp, Ts...>{} }; }
constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
#include <utility>
#include <iostream>
template <typename Tp, std::size_t Size>
class Alphabet
{
private:
Tp m_data[Size ? Size : 1U];
template <Tp T0, Tp ... Ts>
static constexpr bool is_unique ()
{
using unused = bool[];
bool ret = true;
(void)unused{ false, ret &= T0 != Ts... };
return ret;
}
// ground case
template <typename = void>
static constexpr bool are_unique ()
{ return true; }
// recursive case
template <Tp T0, Tp ... Ts>
static constexpr bool are_unique ()
{ return is_unique<T0, Ts...>() && are_unique<Ts...>(); }
public:
template <Tp ... Ts>
constexpr Alphabet (std::integer_sequence<Tp, Ts...> const &)
: m_data{Ts...}
{ static_assert( sizeof...(Ts) <= Size
&& are_unique<Ts...>(), "!" ); }
};
template <typename Tp, Tp ... Ts>
constexpr Alphabet<Tp, sizeof...(Ts)> make_Alphabet ()
{ return { std::integer_sequence<Tp, Ts...>{} }; }
int main()
{
// compilation error (static_assert failed "!")
//constexpr Alphabet<char, 3>
// aba{std::integer_sequence<char, 'a', 'b', 'a'>{}};
// compile
constexpr Alphabet<char, 3>
abc{std::integer_sequence<char, 'a', 'b', 'c'>{}};
// compilation error (static_assert failed "!")
//constexpr auto abca{ make_Alphabet<char, 'a', 'b', 'c', 'a'>() };
// compile
constexpr auto abcd{ make_Alphabet<char, 'a', 'b', 'c', 'd'>() };
}