如何使用 C++ STL 库将 const char[] 数组作为 constexpr 模板参数传递?
How to pass const char[] arrays as constexpr template parameters using C++ STL libraries?
我有 C++ 17
标准的工作代码:
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< int PathLength, const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
int main(int argc, char const *argv[]) {
static constexpr const char path[7] = "c/test";
static_assert( startfindlastslash< 7, path >() == 1, "Fail!" );
}
我想停止 writing/hard 编码 constexpr
数组大小 7
,即,让模板元编程自行推断 constexpr const char[]
数组大小,而不必在任何地方都写出大小。例如,给定如下所示的内容:
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
template<int PathLength>
constexpr const char path[PathLength] = "c/test";
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
当然,上面的代码是完全无效的。但是,它是描述事物的简单方法的一个很好的近似值。
你会如何解决这个问题?你会用 std::array
或 std::string_view
[=79= 替换 constexpr const char path[7] = "c/test";
吗]?
我尝试使用 std::string_view
:
构建此代码
#include <string_view>
template< int PathIndex, std::string_view path >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template< std::string_view path >
constexpr const int startfindlastslash()
{
return findlastslash< path.length() - 1, path >();
}
int main(int argc, char const *argv[]) {
static constexpr std::string_view path{"c/test"};
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
但是编译不通过:
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp:3:43: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter
template< int PathIndex, std::string_view path >
^~~~
test_debugger.cpp:14:28: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter
template< std::string_view path >
^~~~
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:22:47: error: no matching function for call to ‘startfindlastslash<path>()’
static_assert( startfindlastslash< path >() == 1, "Fail!" );
^
test_debugger.cpp:15:21: note: candidate: template<<typeprefixerror>path> constexpr const int startfindlastslash()
constexpr const int startfindlastslash()
^~~~~~~~~~~~~~~~~~
test_debugger.cpp:15:21: note: template argument deduction/substitution failed:
test_debugger.cpp:22:47: note: invalid template non-type parameter
static_assert( startfindlastslash< path >() == 1, "Fail!" );
^
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:3:43: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>')
template< int PathIndex, std::string_view path >
^
test_debugger.cpp:14:28: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>')
template< std::string_view path >
^
test_debugger.cpp:15:21: error: no return statement in constexpr function
constexpr const int startfindlastslash()
^
test_debugger.cpp:22:20: error: no matching function for call to 'startfindlastslash'
static_assert( startfindlastslash< path >() == 1, "Fail!" );
^~~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:15:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path'
constexpr const int startfindlastslash()
^
4 errors generated.
注意:我对查找字符串中的最后一个斜杠不感兴趣,因为算法正在执行此操作。我只是以这个愚蠢的例子为借口,以更好地了解使用 constexpr 模板参数可以做什么和不能做什么。
参考 (C++20) String literals as non-type template parameters example? I found this example code doing something cool with C++ 20
: (https://godbolt.org/z/L0J2K2)
template<unsigned N>
struct FixedString {
char buf[N + 1]{};
constexpr FixedString(char const* s) {
for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;
template<FixedString T>
class Foo {
static constexpr char const* Name = T;
public:
void hello() const;
};
int main() {
Foo<"Hello!"> foo;
foo.hello();
}
我真的需要定义自己的 FixedString
class 吗? C++ STL
(标准模板库)没有任何可以替代此 common/simple 任务的东西?
为了参考,我找到了这个不错的相关第三方库:
- https://github.com/irrequietus/typestring 对于
C++11/14 strings for direct use in template parameter lists, template metaprogramming
.
- https://github.com/hanickadot/compile-time-regular-expressions
A Compile time PCRE (almost) compatible regular expression matcher.
I would like to stop writing/hard coding the constexpr array size 7
在 C++17 中,您可以使用 auto
作为非模板参数并摆脱硬编码 7
:
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash()
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash()
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
int main() {
static constexpr const char path[] = "c/test";
static_assert(startfindlastslash<path>() == 1, "Fail!" );
}
您可能 "protect" auto
SFINAE 或概念 (C++20):
template <typename T, std::size_t N>
constexpr std::true_type is_c_array_impl(const T(&)[N]) { return {}; }
template <typename T>
constexpr std::false_type is_c_array_impl(const T&) { return {}; }
template <typename T>
constexpr auto is_c_array() -> decltype(is_c_array_impl(std::declval<const T&>())) {return {};}
template <typename T>
concept CArrayRef = (bool) is_c_array<const T&>() && std::is_reference_v<T>;
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash() requires (CArrayRef<decltype(path)>)
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash() requires (CArrayRef<decltype(path)>)
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
Do I really need to define my own FixedString
class? The C++ STL (Standard Template Library) does not have anything which can be used instead for this common/simple task?
目前似乎没有等同于FixedString
。
对于它的价值,这是一个使用问题中提出的 FixedString
class 的示例。遗憾的是,这只能用 GCC 9.1 或更高版本编译。甚至现在的 Clang 9 或 Clang trunk 都无法构建它,因为它需要 C++20 特性。
https://godbolt.org/z/eZy2eY(对于 GCC 9.1)
template<unsigned N>
struct FixedString
{
char buf[N + 1]{};
int length = N;
constexpr FixedString(char const* string)
{
for (unsigned index = 0; index < N; ++index) {
buf[index] = string[index];
}
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N>
FixedString(char const (&)[N]) -> FixedString<N - 1>;
template< int PathIndex, FixedString path >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template< FixedString path >
constexpr const int startfindlastslash()
{
return findlastslash< path.length - 1, path >();
}
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< "c/test" >() == 1, "Fail!" );
}
我有 C++ 17
标准的工作代码:
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< int PathLength, const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
int main(int argc, char const *argv[]) {
static constexpr const char path[7] = "c/test";
static_assert( startfindlastslash< 7, path >() == 1, "Fail!" );
}
我想停止 writing/hard 编码 constexpr
数组大小 7
,即,让模板元编程自行推断 constexpr const char[]
数组大小,而不必在任何地方都写出大小。例如,给定如下所示的内容:
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< PathLength - 1, PathLength, path >();
}
template<int PathLength>
constexpr const char path[PathLength] = "c/test";
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
当然,上面的代码是完全无效的。但是,它是描述事物的简单方法的一个很好的近似值。
你会如何解决这个问题?你会用 std::array
或 std::string_view
[=79= 替换 constexpr const char path[7] = "c/test";
吗]?
我尝试使用 std::string_view
:
#include <string_view>
template< int PathIndex, std::string_view path >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template< std::string_view path >
constexpr const int startfindlastslash()
{
return findlastslash< path.length() - 1, path >();
}
int main(int argc, char const *argv[]) {
static constexpr std::string_view path{"c/test"};
static_assert( startfindlastslash< path >() == 1, "Fail!" );
}
但是编译不通过:
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp:3:43: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter template< int PathIndex, std::string_view path > ^~~~ test_debugger.cpp:14:28: error: ‘class std::basic_string_view<char>’ is not a valid type for a template non-type parameter template< std::string_view path > ^~~~ test_debugger.cpp: In function ‘int main(int, const char**)’: test_debugger.cpp:22:47: error: no matching function for call to ‘startfindlastslash<path>()’ static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^ test_debugger.cpp:15:21: note: candidate: template<<typeprefixerror>path> constexpr const int startfindlastslash() constexpr const int startfindlastslash() ^~~~~~~~~~~~~~~~~~ test_debugger.cpp:15:21: note: template argument deduction/substitution failed: test_debugger.cpp:22:47: note: invalid template non-type parameter static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:3:43: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>') template< int PathIndex, std::string_view path > ^ test_debugger.cpp:14:28: error: a non-type template parameter cannot have type 'std::string_view' (aka 'basic_string_view<char>') template< std::string_view path > ^ test_debugger.cpp:15:21: error: no return statement in constexpr function constexpr const int startfindlastslash() ^ test_debugger.cpp:22:20: error: no matching function for call to 'startfindlastslash' static_assert( startfindlastslash< path >() == 1, "Fail!" ); ^~~~~~~~~~~~~~~~~~~~~~~~~~ test_debugger.cpp:15:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path' constexpr const int startfindlastslash() ^ 4 errors generated.
注意:我对查找字符串中的最后一个斜杠不感兴趣,因为算法正在执行此操作。我只是以这个愚蠢的例子为借口,以更好地了解使用 constexpr 模板参数可以做什么和不能做什么。
参考 (C++20) String literals as non-type template parameters example? I found this example code doing something cool with C++ 20
: (https://godbolt.org/z/L0J2K2)
template<unsigned N>
struct FixedString {
char buf[N + 1]{};
constexpr FixedString(char const* s) {
for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;
template<FixedString T>
class Foo {
static constexpr char const* Name = T;
public:
void hello() const;
};
int main() {
Foo<"Hello!"> foo;
foo.hello();
}
我真的需要定义自己的 FixedString
class 吗? C++ STL
(标准模板库)没有任何可以替代此 common/simple 任务的东西?
为了参考,我找到了这个不错的相关第三方库:
- https://github.com/irrequietus/typestring 对于
C++11/14 strings for direct use in template parameter lists, template metaprogramming
. - https://github.com/hanickadot/compile-time-regular-expressions
A Compile time PCRE (almost) compatible regular expression matcher.
I would like to stop writing/hard coding the constexpr array size 7
在 C++17 中,您可以使用 auto
作为非模板参数并摆脱硬编码 7
:
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash()
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash()
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
int main() {
static constexpr const char path[] = "c/test";
static_assert(startfindlastslash<path>() == 1, "Fail!" );
}
您可能 "protect" auto
SFINAE 或概念 (C++20):
template <typename T, std::size_t N>
constexpr std::true_type is_c_array_impl(const T(&)[N]) { return {}; }
template <typename T>
constexpr std::false_type is_c_array_impl(const T&) { return {}; }
template <typename T>
constexpr auto is_c_array() -> decltype(is_c_array_impl(std::declval<const T&>())) {return {};}
template <typename T>
concept CArrayRef = (bool) is_c_array<const T&>() && std::is_reference_v<T>;
template <std::size_t PathIndex, const auto& path>
constexpr std::size_t findlastslash() requires (CArrayRef<decltype(path)>)
{
if constexpr (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template <const auto& path>
constexpr std::size_t startfindlastslash() requires (CArrayRef<decltype(path)>)
{
return findlastslash<std::extent_v<std::remove_reference_t<decltype(path)>> - 1, path >();
}
Do I really need to define my own
FixedString
class? The C++ STL (Standard Template Library) does not have anything which can be used instead for this common/simple task?
目前似乎没有等同于FixedString
。
对于它的价值,这是一个使用问题中提出的 FixedString
class 的示例。遗憾的是,这只能用 GCC 9.1 或更高版本编译。甚至现在的 Clang 9 或 Clang trunk 都无法构建它,因为它需要 C++20 特性。
https://godbolt.org/z/eZy2eY(对于 GCC 9.1)
template<unsigned N>
struct FixedString
{
char buf[N + 1]{};
int length = N;
constexpr FixedString(char const* string)
{
for (unsigned index = 0; index < N; ++index) {
buf[index] = string[index];
}
}
constexpr operator char const*() const { return buf; }
};
template<unsigned N>
FixedString(char const (&)[N]) -> FixedString<N - 1>;
template< int PathIndex, FixedString path >
constexpr const int findlastslash()
{
if constexpr( PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\' ) {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, path>();
}
}
template< FixedString path >
constexpr const int startfindlastslash()
{
return findlastslash< path.length - 1, path >();
}
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< "c/test" >() == 1, "Fail!" );
}