当构造函数输入包含重复项时创建编译器错误
Create compiler error when constructor input contains duplicates
考虑以下 classes:
class Base {
public:
Base(const std::initializer_list<const char*>& words)
: words_(words) {}
std::initializer_list<const char*> words_;
};
class Derived_OK : public Base
{
public:
Derived_OK()
: Base({ "dog", "car", "time"}){}
};
我想通过创建编译时错误来禁止从 Base class 派生的 classes,其中初始值设定项列表包含重复项。例如,不应允许以下 class:
class Derived_BAD : public Base
{
public:
Derived_BAD()
: Base({ "dog", "car", "time", "car"}){} // do not want to allow duplicates at compile time
};
我最初的方法是尝试对 Base class 进行模板化。然而,就我所确定的,我不能使用非类型模板参数,即使在 C++20 中,字符串可以作为参数传递(我相信在 C++20 方法中你只能传递一个字符串)。
我的下一个方法是编写一个 constexpr 函数来确定单词是否唯一
constexpr bool unique_words(const std::initializer_list<const char*>& words);
然后重写 Base class 如下:
class Base {
public:
constexpr Base(const std::initializer_list<const char*>& words)
: words_(words)
{
static_assert(unique_words(words));
}
std::initializer_list<const char*> words_;
};
尽管此函数在 class 之外工作,但在 Base 构造函数内部,编译器告诉我不能将构造函数参数的值用作常量。当然,我可以写一个 运行 时间检查,但我真的不希望在编译时在初始化列表中创建重复的单词。这可能吗?
要检查任何内容,您必须 constexpr 构造 class。要在 constexpr 函数内触发编译时错误,您可以抛出,参见 .
#include <initializer_list>
#include <type_traits>
#include <array>
#include <stdexcept>
template<std::size_t N>
constexpr bool unique_words(const std::array<const char*, N>& words) {
// TODO: implement real logic here
return words[0][0] == 'd';
}
template<std::size_t N>
struct Base {
constexpr Base(const std::array<const char*, N>& words)
: words_(words)
{
if (!unique_words<N>(words)) {
throw std::invalid_argument("Base must take unique words!");
}
}
std::array<const char*, N> words_;
};
struct Derived_BAD : public Base<4> {
constexpr Derived_BAD() : Base{{"e", "car", "time", "car"}} {}
};
int main() {
constexpr Derived_BAD var; // compile-time error - can't throw in constexpr
Derived_BAD var2; // will throw at runtime
}
不要将 std::initializer_list
存储在您的 class 中!!参见 。
当不是构造constexpr
时,无法进行编译时检查。如果您真的想要检查这种情况,您可以使用 GNU __builtin_constant_p
扩展并启用编译器优化,请参阅 GNU 文档和 and 。
My initial approach was to try templating the Base class. However, as far as I have determined I cannot use non-type template parameters, even in C++20 where a string can be passed as a parameter (I believe you can pass only one string in the C++20 approach).
当然,在 C++20 中,您可以将多个字符串作为非类型模板参数。
本质上,您可以做的是有一个名为 fixed_string
的小包装器 class,它可以隐式转换为 const char*
或从 const char*
转换。从 C++20 开始,可以将简单的结构作为非类型模板参数,因为这样可以使用这个轻量级包装器 class 作为非类型模板参数。
这是一个 C++20 实现,可用于实现您想要做的事情:
#include <cstddef>
#include <type_traits>
// The wrapper class in question
template <std::size_t N>
struct fixed_string {
char str[N + 1] {};
constexpr fixed_string(const char* X) {
for (std::size_t i = 0; i < N; i++)
str[i] = X[i];
}
constexpr operator const char*() const {
return str;
}
};
template <std::size_t N>
fixed_string(const char (&)[N]) -> fixed_string<N - 1>;
// Stores a list of 'fixed_string's
template <fixed_string ...Strings>
struct fixed_string_list;
// Concatenates two 'fixed_string_list's together
template <typename, typename>
struct fixed_string_list_concat;
template <fixed_string ...Strings1, fixed_string ...Strings2>
struct fixed_string_list_concat<fixed_string_list<Strings1...>, fixed_string_list<Strings2...>> {
using type = fixed_string_list<Strings1..., Strings2...>;
};
// Fetches the string at the specified index in the 'fixed_string_list', the required string is put inside a 'fixed_string_list' and then returned back
template <typename, std::size_t>
struct fixed_string_list_get;
template <std::size_t I, fixed_string String1, fixed_string ...Strings>
struct fixed_string_list_get<fixed_string_list<String1, Strings...>, I> {
using type = typename fixed_string_list_get<fixed_string_list<Strings...>, I - 1>::type;
};
template <fixed_string String1, fixed_string ...Strings>
struct fixed_string_list_get<fixed_string_list<String1, Strings...>, 0> {
using type = fixed_string_list<String1>;
};
// Trims the list in the range [From, To)
template <typename, std::size_t, std::size_t>
struct fixed_string_list_trim;
template <std::size_t From, std::size_t To, fixed_string ...Strings>
requires (From < To)
struct fixed_string_list_trim<fixed_string_list<Strings...>, From, To> {
using type = typename fixed_string_list_concat<typename fixed_string_list_get<fixed_string_list<Strings...>, From>::type, typename fixed_string_list_trim<fixed_string_list<Strings...>, From + 1, To>::type>::type;
};
template <std::size_t From, std::size_t To, fixed_string ...Strings>
requires (From >= To)
struct fixed_string_list_trim<fixed_string_list<Strings...>, From, To> {
using type = fixed_string_list<>;
};
// Returns the 'fixed_string_list' excluding the string at the specified index
template <typename, std::size_t>
struct fixed_string_list_exclude;
template <std::size_t I, fixed_string ...Strings>
requires (I > 0 && I < sizeof...(Strings) - 1)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_concat<typename fixed_string_list_trim<fixed_string_list<Strings...>, 0, I>::type, typename fixed_string_list_trim<fixed_string_list<Strings...>, I + 1, sizeof...(Strings) - I + 1>::type>::type;
};
template <std::size_t I, fixed_string ...Strings>
requires (I == 0)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_trim<fixed_string_list<Strings...>, 1, sizeof...(Strings)>::type;
};
template <std::size_t I, fixed_string ...Strings>
requires (I == sizeof...(Strings) - 1)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_trim<fixed_string_list<Strings...>, 0, I>::type;
};
// Checks whether a 'fixed_string_list' contains a given string, the string to be found must also be within a 'fixed_string_list'
template <typename, typename>
struct fixed_string_list_contains;
template <fixed_string String, fixed_string ...Strings>
struct fixed_string_list_contains<fixed_string_list<Strings...>, fixed_string_list<String>> : std::bool_constant<((String == Strings) || ...)> {};
// Implementation detail for 'is_fixed_string_list_unique'
template <typename, std::size_t, std::size_t>
struct is_fixed_string_list_unique_impl;
template <std::size_t I, std::size_t Limit, fixed_string ...Strings>
struct is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I, Limit> : std::bool_constant<!fixed_string_list_contains<typename fixed_string_list_exclude<fixed_string_list<Strings...>, I>::type, typename fixed_string_list_get<fixed_string_list<Strings...>, I>::type>::value && is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I + 1, Limit>::value> {};
template <std::size_t I, fixed_string ...Strings>
struct is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I, I> : std::true_type {};
// Checks whether the given 'fixed_string_list' has no repeating strings inside
template <typename>
struct is_fixed_string_list_unique;
template <fixed_string ...Strings>
requires (sizeof...(Strings) > 1)
struct is_fixed_string_list_unique<fixed_string_list<Strings...>> : std::bool_constant<is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, 0, sizeof...(Strings)>::value> {};
template <fixed_string ...Strings>
requires (sizeof...(Strings) <= 1)
struct is_fixed_string_list_unique<fixed_string_list<Strings...>> : std::true_type {};
现在你终于可以做这样的事情了:
template <fixed_string ...Strings>
struct Base {
static_assert(is_fixed_string_list_unique<fixed_string_list<Strings...>>(), "Duplicate strings are not allowed!");
};
struct Derived_OK : public Base<"dog", "car", "time"> {};
// Results in a static assertion failure: "Duplicate strings are not allowed!"
struct Derived_BAD : public Base<"dog", "car", "time", "car"> {};
这里有一个 link 您可以自己尝试一下:
考虑以下 classes:
class Base {
public:
Base(const std::initializer_list<const char*>& words)
: words_(words) {}
std::initializer_list<const char*> words_;
};
class Derived_OK : public Base
{
public:
Derived_OK()
: Base({ "dog", "car", "time"}){}
};
我想通过创建编译时错误来禁止从 Base class 派生的 classes,其中初始值设定项列表包含重复项。例如,不应允许以下 class:
class Derived_BAD : public Base
{
public:
Derived_BAD()
: Base({ "dog", "car", "time", "car"}){} // do not want to allow duplicates at compile time
};
我最初的方法是尝试对 Base class 进行模板化。然而,就我所确定的,我不能使用非类型模板参数,即使在 C++20 中,字符串可以作为参数传递(我相信在 C++20 方法中你只能传递一个字符串)。
我的下一个方法是编写一个 constexpr 函数来确定单词是否唯一
constexpr bool unique_words(const std::initializer_list<const char*>& words);
然后重写 Base class 如下:
class Base {
public:
constexpr Base(const std::initializer_list<const char*>& words)
: words_(words)
{
static_assert(unique_words(words));
}
std::initializer_list<const char*> words_;
};
尽管此函数在 class 之外工作,但在 Base 构造函数内部,编译器告诉我不能将构造函数参数的值用作常量。当然,我可以写一个 运行 时间检查,但我真的不希望在编译时在初始化列表中创建重复的单词。这可能吗?
要检查任何内容,您必须 constexpr 构造 class。要在 constexpr 函数内触发编译时错误,您可以抛出,参见
#include <initializer_list>
#include <type_traits>
#include <array>
#include <stdexcept>
template<std::size_t N>
constexpr bool unique_words(const std::array<const char*, N>& words) {
// TODO: implement real logic here
return words[0][0] == 'd';
}
template<std::size_t N>
struct Base {
constexpr Base(const std::array<const char*, N>& words)
: words_(words)
{
if (!unique_words<N>(words)) {
throw std::invalid_argument("Base must take unique words!");
}
}
std::array<const char*, N> words_;
};
struct Derived_BAD : public Base<4> {
constexpr Derived_BAD() : Base{{"e", "car", "time", "car"}} {}
};
int main() {
constexpr Derived_BAD var; // compile-time error - can't throw in constexpr
Derived_BAD var2; // will throw at runtime
}
不要将 std::initializer_list
存储在您的 class 中!!参见
当不是构造constexpr
时,无法进行编译时检查。如果您真的想要检查这种情况,您可以使用 GNU __builtin_constant_p
扩展并启用编译器优化,请参阅 GNU 文档和
My initial approach was to try templating the Base class. However, as far as I have determined I cannot use non-type template parameters, even in C++20 where a string can be passed as a parameter (I believe you can pass only one string in the C++20 approach).
当然,在 C++20 中,您可以将多个字符串作为非类型模板参数。
本质上,您可以做的是有一个名为 fixed_string
的小包装器 class,它可以隐式转换为 const char*
或从 const char*
转换。从 C++20 开始,可以将简单的结构作为非类型模板参数,因为这样可以使用这个轻量级包装器 class 作为非类型模板参数。
这是一个 C++20 实现,可用于实现您想要做的事情:
#include <cstddef>
#include <type_traits>
// The wrapper class in question
template <std::size_t N>
struct fixed_string {
char str[N + 1] {};
constexpr fixed_string(const char* X) {
for (std::size_t i = 0; i < N; i++)
str[i] = X[i];
}
constexpr operator const char*() const {
return str;
}
};
template <std::size_t N>
fixed_string(const char (&)[N]) -> fixed_string<N - 1>;
// Stores a list of 'fixed_string's
template <fixed_string ...Strings>
struct fixed_string_list;
// Concatenates two 'fixed_string_list's together
template <typename, typename>
struct fixed_string_list_concat;
template <fixed_string ...Strings1, fixed_string ...Strings2>
struct fixed_string_list_concat<fixed_string_list<Strings1...>, fixed_string_list<Strings2...>> {
using type = fixed_string_list<Strings1..., Strings2...>;
};
// Fetches the string at the specified index in the 'fixed_string_list', the required string is put inside a 'fixed_string_list' and then returned back
template <typename, std::size_t>
struct fixed_string_list_get;
template <std::size_t I, fixed_string String1, fixed_string ...Strings>
struct fixed_string_list_get<fixed_string_list<String1, Strings...>, I> {
using type = typename fixed_string_list_get<fixed_string_list<Strings...>, I - 1>::type;
};
template <fixed_string String1, fixed_string ...Strings>
struct fixed_string_list_get<fixed_string_list<String1, Strings...>, 0> {
using type = fixed_string_list<String1>;
};
// Trims the list in the range [From, To)
template <typename, std::size_t, std::size_t>
struct fixed_string_list_trim;
template <std::size_t From, std::size_t To, fixed_string ...Strings>
requires (From < To)
struct fixed_string_list_trim<fixed_string_list<Strings...>, From, To> {
using type = typename fixed_string_list_concat<typename fixed_string_list_get<fixed_string_list<Strings...>, From>::type, typename fixed_string_list_trim<fixed_string_list<Strings...>, From + 1, To>::type>::type;
};
template <std::size_t From, std::size_t To, fixed_string ...Strings>
requires (From >= To)
struct fixed_string_list_trim<fixed_string_list<Strings...>, From, To> {
using type = fixed_string_list<>;
};
// Returns the 'fixed_string_list' excluding the string at the specified index
template <typename, std::size_t>
struct fixed_string_list_exclude;
template <std::size_t I, fixed_string ...Strings>
requires (I > 0 && I < sizeof...(Strings) - 1)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_concat<typename fixed_string_list_trim<fixed_string_list<Strings...>, 0, I>::type, typename fixed_string_list_trim<fixed_string_list<Strings...>, I + 1, sizeof...(Strings) - I + 1>::type>::type;
};
template <std::size_t I, fixed_string ...Strings>
requires (I == 0)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_trim<fixed_string_list<Strings...>, 1, sizeof...(Strings)>::type;
};
template <std::size_t I, fixed_string ...Strings>
requires (I == sizeof...(Strings) - 1)
struct fixed_string_list_exclude<fixed_string_list<Strings...>, I> {
using type = typename fixed_string_list_trim<fixed_string_list<Strings...>, 0, I>::type;
};
// Checks whether a 'fixed_string_list' contains a given string, the string to be found must also be within a 'fixed_string_list'
template <typename, typename>
struct fixed_string_list_contains;
template <fixed_string String, fixed_string ...Strings>
struct fixed_string_list_contains<fixed_string_list<Strings...>, fixed_string_list<String>> : std::bool_constant<((String == Strings) || ...)> {};
// Implementation detail for 'is_fixed_string_list_unique'
template <typename, std::size_t, std::size_t>
struct is_fixed_string_list_unique_impl;
template <std::size_t I, std::size_t Limit, fixed_string ...Strings>
struct is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I, Limit> : std::bool_constant<!fixed_string_list_contains<typename fixed_string_list_exclude<fixed_string_list<Strings...>, I>::type, typename fixed_string_list_get<fixed_string_list<Strings...>, I>::type>::value && is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I + 1, Limit>::value> {};
template <std::size_t I, fixed_string ...Strings>
struct is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, I, I> : std::true_type {};
// Checks whether the given 'fixed_string_list' has no repeating strings inside
template <typename>
struct is_fixed_string_list_unique;
template <fixed_string ...Strings>
requires (sizeof...(Strings) > 1)
struct is_fixed_string_list_unique<fixed_string_list<Strings...>> : std::bool_constant<is_fixed_string_list_unique_impl<fixed_string_list<Strings...>, 0, sizeof...(Strings)>::value> {};
template <fixed_string ...Strings>
requires (sizeof...(Strings) <= 1)
struct is_fixed_string_list_unique<fixed_string_list<Strings...>> : std::true_type {};
现在你终于可以做这样的事情了:
template <fixed_string ...Strings>
struct Base {
static_assert(is_fixed_string_list_unique<fixed_string_list<Strings...>>(), "Duplicate strings are not allowed!");
};
struct Derived_OK : public Base<"dog", "car", "time"> {};
// Results in a static assertion failure: "Duplicate strings are not allowed!"
struct Derived_BAD : public Base<"dog", "car", "time", "car"> {};
这里有一个 link 您可以自己尝试一下: