如何将 const char[] 数组作为 constexpr 传递给模板?
How to pass a const char[] array as constexpr to a template?
为了探索 C++ 的抽象能力,我从 on this other question :
拿了这个例子
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template<int PathLength, int I = PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
if constexpr (I == 0) {
return 0;
} else {
if (path[I - 1] == '/' || path[I - 1] == '\') {
return I;
}
return findlastslash<PathLength, I - 1>(path);
}
}
int main(int argc, char const *argv[]) {
STATIC_ASSERT( findlastslash( "c/test" ) == 2 );
}
这个例子,我试图通过从函数 findlastslash_impl
[= 中删除 constexpr 参数 I
来改进它134=]。我想删除 I
constexpr 参数,因为它使递归总是从 const char[]
字符串大小下降到0:(用 clang
编译它并使用特殊标志向我们展示模板扩展)
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
template <int PathLength, int I = PathLength>
constexpr const int findlastslash(const char (&path)[PathLength]) {
if (I == 0) {
return 0;
} else {
if (path[I - 1] == '/' || path[I - 1] == '\') {
return I;
}
return findlastslash<PathLength, I - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 7>(const char (&path)[7]) {
if (7 == 0)
;
else {
if (path[7 - 1] == '/' || path[7 - 1] == '\') {
return 7;
}
return findlastslash<7, 7 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 6>(const char (&path)[7]) {
if (6 == 0)
;
else {
if (path[6 - 1] == '/' || path[6 - 1] == '\') {
return 6;
}
return findlastslash<7, 6 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 5>(const char (&path)[7]) {
if (5 == 0)
;
else {
if (path[5 - 1] == '/' || path[5 - 1] == '\') {
return 5;
}
return findlastslash<7, 5 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 4>(const char (&path)[7]) {
if (4 == 0)
;
else {
if (path[4 - 1] == '/' || path[4 - 1] == '\') {
return 4;
}
return findlastslash<7, 4 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 3>(const char (&path)[7]) {
if (3 == 0)
;
else {
if (path[3 - 1] == '/' || path[3 - 1] == '\') {
return 3;
}
return findlastslash<7, 3 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 2>(const char (&path)[7]) {
if (2 == 0)
;
else {
if (path[2 - 1] == '/' || path[2 - 1] == '\') {
return 2;
}
return findlastslash<7, 2 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 1>(const char (&path)[7]) {
if (1 == 0)
;
else {
if (path[1 - 1] == '/' || path[1 - 1] == '\') {
return 1;
}
return findlastslash<7, 1 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 0>(const char (&path)[7]) {
if (0 == 0) {
return 0;
}
}
int main(int argc, const char *argv[]) {
static_assert(findlastslash("c/test") == 2,
"findlastslash( \"c/test\" ) == 2");
}
然后,如果将函数参数 path
转换为 constexpr,我可以删除 I
constexpr 参数,我只能将 findlastslash_impl
函数递归扩展到最后一个斜杠的位置,而不是总是从 const char[]
字符串大小减小到 0。
像 这样的问题只是另一个问题,说我做不到,并建议另一个 hack。当 C++20 或 C++23 与这些提案之一一起发布时,可能会写下这个问题的答案:
通常会发现像下面这样的东西,效果很好:(Passing an array by reference to template function in c++)
template<class T>
T sum_array(T (&a)[10], int size)
{ ...
但我想做类似下面这样的事情,以便能够在 if constexpr
:
中使用数组
template<class T, T (&a)[10]>
T sum_array(int size)
{ ...
在这里,我有一个例子,我正在尝试应用这个概念:
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
return PathIndex;
}
return findlastslash<PathIndex - 1, PathLength, path>();
}
int main(int argc, char const *argv[])
{
STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 );
}
用C++17
标准编译它,我得到编译器说:error: ‘"c/test"’ is not a valid template argument for type ‘const char*’ because string literals can never be used in this context
如何将 const char[]
数组作为常量传递给 findlastslash()
?
最初我尝试传递 const char[]
数组作为参考:
template< int PathIndex, int PathLength, const char (&path)[PathLength] >
constexpr const int findlastslash()
{
导致编译器抛出错误:
error: ‘const char (& path)[7]’ is not a valid template argument for type ‘const char (&)[7]’ because a reference variable does not have a constant address
note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path'
这些是第一个尝试使用 const char[]
数组作为模板参数引用的示例的编译器错误:template< int PathIndex, int PathLength, const char (&path)[PathLength] >
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:14:52: error: no matching function for call to ‘findlastslash<6, 6, "c/test">()’
STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 );
^
test_debugger.cpp:1:42: note: in definition of macro ‘STATIC_ASSERT’
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
^~~~~~~~~~~
test_debugger.cpp:4:21: note: candidate: template<int PathIndex, int PathLength, const char* path> constexpr const int findlastslash()
constexpr const int findlastslash()
^~~~~~~~~~~~~
test_debugger.cpp:4:21: note: template argument deduction/substitution failed:
test_debugger.cpp:14:52: error: ‘"c/test"’ is not a valid template argument for type ‘const char*’ because string literals can never be used in this context
STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 );
^
test_debugger.cpp:1:42: note: in definition of macro ‘STATIC_ASSERT’
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
^~~~~~~~~~~
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:14:20: error: no matching function for call to 'findlastslash'
STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:1:42: note: expanded from macro 'STATIC_ASSERT'
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
^~~~~~~~~~~
test_debugger.cpp:4:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path'
constexpr const int findlastslash()
^
1 error generated.
更新
在我尝试结合 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;
}
return findlastslash<PathIndex - 1, PathLength, path>();
}
constexpr const char path[] = "c/test";
int main(int argc, char const *argv[])
{
static_assert( findlastslash< 6, 6, path >() == 2, "Fail!" );
}
悲惨地失败了:
clc g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:15:5: error: static assertion failed: Fail!
static_assert( findlastslash< 6, 6, path >() == 2, "Fail!" );
^~~~~~~~~~~~~
test_debugger.cpp: In instantiation of ‘constexpr const int findlastslash() [with int PathIndex = -898; int PathLength = 6; const char* path = (& path)]’:
test_debugger.cpp:8:58: recursively required from ‘constexpr const int findlastslash() [with int PathIndex = 1; int PathLength = 6; const char* path = (& path)]’
test_debugger.cpp:8:58: required from here
test_debugger.cpp:8:58: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return findlastslash<PathIndex - 1, PathLength, path>();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
compilation terminated.
即使使用 if constexpr
,C++ 也无法通过自身检查 PathIndex
参数来停止递归。
一些相关问题:
- C++11 Local static values not working as template arguments
- Template on address of variable with static storage
雅虎。我设法做了一些与 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>();
}
}
int main(int argc, char const *argv[]) {
static constexpr const char path[] = "c/test";
static_assert( findlastslash< 6, 7, path >() == 1, "Fail!" );
}
扩展为:
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
template <int PathIndex, int PathLength, const char (path)[PathLength]>
constexpr const int findlastslash()
{
if (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
} else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template<>
constexpr const int findlastslash<6, 7, &path>()
{
if (6 < 1 || path[6] == '/' || path[6] == '\');
else {
return findlastslash<6 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<5, 7, &path>()
{
if (5 < 1 || path[5] == '/' || path[5] == '\');
else {
return findlastslash<5 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<4, 7, &path>()
{
if (4 < 1 || path[4] == '/' || path[4] == '\');
else {
return findlastslash<4 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<3, 7, &path>()
{
if (3 < 1 || path[3] == '/' || path[3] == '\');
else {
return findlastslash<3 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<2, 7, &path>()
{
if (2 < 1 || path[2] == '/' || path[2] == '\');
else {
return findlastslash<2 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<1, 7, &path>()
{
if (1 < 1 || path[1] == '/' || path[1] == '\') {
return 1;
}
}
int main(int argc, const char *argv[]) {
static constexpr const char path[] = "c/test";
static_assert(findlastslash<6, 7, path>() == 1, "Fail!");
}
虽然这不是理想的解决方案,但这是 "works" 可用的 C++
。
最好的答案是 C++
标准,它允许我们编写易于维护的代码,如下所示:
template< const char (path)[PathLength], int PathIndex >
constexpr const int findlastslash()
{
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< path, PathLength >();
}
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< "c/test" >() == 1, "Fail" );
}
然而,这么好的代码却惨遭失败:
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:1:29: error: use of undeclared identifier 'PathLength'
template< const char (path)[PathLength], int PathIndex >
^
test_debugger.cpp:4:23: error: subscripted value is not an array, pointer, or vector
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
~~~~^~~~~~~~~~~~~~
test_debugger.cpp:4:53: error: subscripted value is not an array, pointer, or vector
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
~~~~^~~~~~~~~~~~~~
test_debugger.cpp:8:45: error: use of undeclared identifier 'PathLength'
return findlastslash<PathIndex - 1, PathLength, path>();
^
test_debugger.cpp:12:29: error: use of undeclared identifier 'PathLength'
template< const char (path)[PathLength] >
^
test_debugger.cpp:15:33: error: use of undeclared identifier 'PathLength'
return findlastslash< path, PathLength >();
^
test_debugger.cpp:13:21: error: no return statement in constexpr function
constexpr const int startfindlastslash()
^
test_debugger.cpp:19:20: error: no matching function for call to 'startfindlastslash'
static_assert( startfindlastslash< "c/test" >() == 1, "Fail" );
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
test_debugger.cpp:13:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path'
constexpr const int startfindlastslash()
^
8 errors generated.
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp:1:29: error: ‘PathLength’ was not declared in this scope
template< const char (path)[PathLength], int PathIndex >
^~~~~~~~~~
test_debugger.cpp: In function ‘constexpr const int findlastslash()’:
test_debugger.cpp:4:19: error: ‘path’ was not declared in this scope
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
^~~~
test_debugger.cpp:8:45: error: ‘PathLength’ was not declared in this scope
return findlastslash<PathIndex - 1, PathLength, path>();
^~~~~~~~~~
test_debugger.cpp:8:45: note: suggested alternative: ‘PathIndex’
return findlastslash<PathIndex - 1, PathLength, path>();
^~~~~~~~~~
PathIndex
test_debugger.cpp: At global scope:
test_debugger.cpp:12:29: error: ‘PathLength’ was not declared in this scope
template< const char (path)[PathLength] >
^~~~~~~~~~
test_debugger.cpp: In function ‘constexpr const int startfindlastslash()’:
test_debugger.cpp:15:27: error: ‘path’ was not declared in this scope
return findlastslash< path, PathLength >();
^~~~
test_debugger.cpp:15:33: error: ‘PathLength’ was not declared in this scope
return findlastslash< path, PathLength >();
^~~~~~~~~~
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:19:51: error: no matching function for call to ‘startfindlastslash<"c/test">()’
static_assert( startfindlastslash< "c/test" >() == 1, "Fail" );
^
test_debugger.cpp:13:21: note: candidate: template<<declaration error> > constexpr const int startfindlastslash()
constexpr const int startfindlastslash()
^~~~~~~~~~~~~~~~~~
test_debugger.cpp:13:21: note: template argument deduction/substitution failed:
来自
的另一个示例
constexpr unsigned int requires_inRange(unsigned int i, unsigned int len) {
return i >= len ? throw i : i;
}
class StrWrap
{
unsigned size_;
const char * begin_;
public:
template< unsigned N >
constexpr StrWrap( const char(&arr)[N] ) : begin_(arr), size_(N - 1) {
static_assert( N >= 1, "not a string literal");
}
constexpr char operator[]( unsigned i ) {
return requires_inRange(i, size_), begin_[i];
}
constexpr operator const char *() {
return begin_;
}
constexpr unsigned size() {
return size_;
}
};
constexpr unsigned count( StrWrap str, char c, unsigned i = 0, unsigned ans = 0 )
{
return i == str.size() ? ans :
str[i] == c ? count(str, c, i + 1, ans + 1) :
count(str, c, i + 1, ans);
}
int main(int argc, char const *argv[])
{
static_assert( count("dude", 'd' ) == 2, "d != 2" );
return 0;
}
为了探索 C++ 的抽象能力,我从
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template<int PathLength, int I = PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
if constexpr (I == 0) {
return 0;
} else {
if (path[I - 1] == '/' || path[I - 1] == '\') {
return I;
}
return findlastslash<PathLength, I - 1>(path);
}
}
int main(int argc, char const *argv[]) {
STATIC_ASSERT( findlastslash( "c/test" ) == 2 );
}
这个例子,我试图通过从函数 findlastslash_impl
[= 中删除 constexpr 参数 I
来改进它134=]。我想删除 I
constexpr 参数,因为它使递归总是从 const char[]
字符串大小下降到0:(用 clang
编译它并使用特殊标志向我们展示模板扩展)
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
template <int PathLength, int I = PathLength>
constexpr const int findlastslash(const char (&path)[PathLength]) {
if (I == 0) {
return 0;
} else {
if (path[I - 1] == '/' || path[I - 1] == '\') {
return I;
}
return findlastslash<PathLength, I - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 7>(const char (&path)[7]) {
if (7 == 0)
;
else {
if (path[7 - 1] == '/' || path[7 - 1] == '\') {
return 7;
}
return findlastslash<7, 7 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 6>(const char (&path)[7]) {
if (6 == 0)
;
else {
if (path[6 - 1] == '/' || path[6 - 1] == '\') {
return 6;
}
return findlastslash<7, 6 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 5>(const char (&path)[7]) {
if (5 == 0)
;
else {
if (path[5 - 1] == '/' || path[5 - 1] == '\') {
return 5;
}
return findlastslash<7, 5 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 4>(const char (&path)[7]) {
if (4 == 0)
;
else {
if (path[4 - 1] == '/' || path[4 - 1] == '\') {
return 4;
}
return findlastslash<7, 4 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 3>(const char (&path)[7]) {
if (3 == 0)
;
else {
if (path[3 - 1] == '/' || path[3 - 1] == '\') {
return 3;
}
return findlastslash<7, 3 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 2>(const char (&path)[7]) {
if (2 == 0)
;
else {
if (path[2 - 1] == '/' || path[2 - 1] == '\') {
return 2;
}
return findlastslash<7, 2 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 1>(const char (&path)[7]) {
if (1 == 0)
;
else {
if (path[1 - 1] == '/' || path[1 - 1] == '\') {
return 1;
}
return findlastslash<7, 1 - 1>(path);
}
}
template<>
constexpr const int findlastslash<7, 0>(const char (&path)[7]) {
if (0 == 0) {
return 0;
}
}
int main(int argc, const char *argv[]) {
static_assert(findlastslash("c/test") == 2,
"findlastslash( \"c/test\" ) == 2");
}
然后,如果将函数参数 path
转换为 constexpr,我可以删除 I
constexpr 参数,我只能将 findlastslash_impl
函数递归扩展到最后一个斜杠的位置,而不是总是从 const char[]
字符串大小减小到 0。
像
通常会发现像下面这样的东西,效果很好:(Passing an array by reference to template function in c++)
template<class T>
T sum_array(T (&a)[10], int size)
{ ...
但我想做类似下面这样的事情,以便能够在 if constexpr
:
template<class T, T (&a)[10]>
T sum_array(int size)
{ ...
在这里,我有一个例子,我正在尝试应用这个概念:
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template< int PathIndex, int PathLength, const char (path)[PathLength] >
constexpr const int findlastslash()
{
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
return PathIndex;
}
return findlastslash<PathIndex - 1, PathLength, path>();
}
int main(int argc, char const *argv[])
{
STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 );
}
用C++17
标准编译它,我得到编译器说:error: ‘"c/test"’ is not a valid template argument for type ‘const char*’ because string literals can never be used in this context
如何将 const char[]
数组作为常量传递给 findlastslash()
?
最初我尝试传递 const char[]
数组作为参考:
template< int PathIndex, int PathLength, const char (&path)[PathLength] >
constexpr const int findlastslash()
{
导致编译器抛出错误:
error: ‘const char (& path)[7]’ is not a valid template argument for type ‘const char (&)[7]’ because a reference variable does not have a constant address
note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path'
这些是第一个尝试使用 const char[]
数组作为模板参数引用的示例的编译器错误:template< int PathIndex, int PathLength, const char (&path)[PathLength] >
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp: In function ‘int main(int, const char**)’: test_debugger.cpp:14:52: error: no matching function for call to ‘findlastslash<6, 6, "c/test">()’ STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 ); ^ test_debugger.cpp:1:42: note: in definition of macro ‘STATIC_ASSERT’ #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) ^~~~~~~~~~~ test_debugger.cpp:4:21: note: candidate: template<int PathIndex, int PathLength, const char* path> constexpr const int findlastslash() constexpr const int findlastslash() ^~~~~~~~~~~~~ test_debugger.cpp:4:21: note: template argument deduction/substitution failed: test_debugger.cpp:14:52: error: ‘"c/test"’ is not a valid template argument for type ‘const char*’ because string literals can never be used in this context STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 ); ^ test_debugger.cpp:1:42: note: in definition of macro ‘STATIC_ASSERT’ #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) ^~~~~~~~~~~
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:14:20: error: no matching function for call to 'findlastslash' STATIC_ASSERT( findlastslash< 6, 6, "c/test" >() == 2 ); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test_debugger.cpp:1:42: note: expanded from macro 'STATIC_ASSERT' #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) ^~~~~~~~~~~ test_debugger.cpp:4:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path' constexpr const int findlastslash() ^ 1 error generated.
更新
在我尝试结合 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;
}
return findlastslash<PathIndex - 1, PathLength, path>();
}
constexpr const char path[] = "c/test";
int main(int argc, char const *argv[])
{
static_assert( findlastslash< 6, 6, path >() == 2, "Fail!" );
}
悲惨地失败了:
clc g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:15:5: error: static assertion failed: Fail!
static_assert( findlastslash< 6, 6, path >() == 2, "Fail!" );
^~~~~~~~~~~~~
test_debugger.cpp: In instantiation of ‘constexpr const int findlastslash() [with int PathIndex = -898; int PathLength = 6; const char* path = (& path)]’:
test_debugger.cpp:8:58: recursively required from ‘constexpr const int findlastslash() [with int PathIndex = 1; int PathLength = 6; const char* path = (& path)]’
test_debugger.cpp:8:58: required from here
test_debugger.cpp:8:58: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return findlastslash<PathIndex - 1, PathLength, path>();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~
compilation terminated.
即使使用 if constexpr
,C++ 也无法通过自身检查 PathIndex
参数来停止递归。
一些相关问题:
- C++11 Local static values not working as template arguments
- Template on address of variable with static storage
雅虎。我设法做了一些与 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>();
}
}
int main(int argc, char const *argv[]) {
static constexpr const char path[] = "c/test";
static_assert( findlastslash< 6, 7, path >() == 1, "Fail!" );
}
扩展为:
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
template <int PathIndex, int PathLength, const char (path)[PathLength]>
constexpr const int findlastslash()
{
if (PathIndex < 1 || path[PathIndex] == '/' || path[PathIndex] == '\') {
return PathIndex;
} else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template<>
constexpr const int findlastslash<6, 7, &path>()
{
if (6 < 1 || path[6] == '/' || path[6] == '\');
else {
return findlastslash<6 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<5, 7, &path>()
{
if (5 < 1 || path[5] == '/' || path[5] == '\');
else {
return findlastslash<5 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<4, 7, &path>()
{
if (4 < 1 || path[4] == '/' || path[4] == '\');
else {
return findlastslash<4 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<3, 7, &path>()
{
if (3 < 1 || path[3] == '/' || path[3] == '\');
else {
return findlastslash<3 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<2, 7, &path>()
{
if (2 < 1 || path[2] == '/' || path[2] == '\');
else {
return findlastslash<2 - 1, 7, path>();
}
}
template<>
constexpr const int findlastslash<1, 7, &path>()
{
if (1 < 1 || path[1] == '/' || path[1] == '\') {
return 1;
}
}
int main(int argc, const char *argv[]) {
static constexpr const char path[] = "c/test";
static_assert(findlastslash<6, 7, path>() == 1, "Fail!");
}
虽然这不是理想的解决方案,但这是 "works" 可用的 C++
。
最好的答案是 C++
标准,它允许我们编写易于维护的代码,如下所示:
template< const char (path)[PathLength], int PathIndex >
constexpr const int findlastslash()
{
if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') {
return PathIndex;
}
else {
return findlastslash<PathIndex - 1, PathLength, path>();
}
}
template< const char (path)[PathLength] >
constexpr const int startfindlastslash()
{
return findlastslash< path, PathLength >();
}
int main(int argc, char const *argv[]) {
static_assert( startfindlastslash< "c/test" >() == 1, "Fail" );
}
然而,这么好的代码却惨遭失败:
clang++ -Xclang -ast-print -fsyntax-only --std=c++17 test_debugger.cpp > main.exe
test_debugger.cpp:1:29: error: use of undeclared identifier 'PathLength' template< const char (path)[PathLength], int PathIndex > ^ test_debugger.cpp:4:23: error: subscripted value is not an array, pointer, or vector if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') { ~~~~^~~~~~~~~~~~~~ test_debugger.cpp:4:53: error: subscripted value is not an array, pointer, or vector if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') { ~~~~^~~~~~~~~~~~~~ test_debugger.cpp:8:45: error: use of undeclared identifier 'PathLength' return findlastslash<PathIndex - 1, PathLength, path>(); ^ test_debugger.cpp:12:29: error: use of undeclared identifier 'PathLength' template< const char (path)[PathLength] > ^ test_debugger.cpp:15:33: error: use of undeclared identifier 'PathLength' return findlastslash< path, PathLength >(); ^ test_debugger.cpp:13:21: error: no return statement in constexpr function constexpr const int startfindlastslash() ^ test_debugger.cpp:19:20: error: no matching function for call to 'startfindlastslash' static_assert( startfindlastslash< "c/test" >() == 1, "Fail" ); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test_debugger.cpp:13:21: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'path' constexpr const int startfindlastslash() ^ 8 errors generated.
g++ -o main.exe --std=c++17 test_debugger.cpp
test_debugger.cpp:1:29: error: ‘PathLength’ was not declared in this scope template< const char (path)[PathLength], int PathIndex > ^~~~~~~~~~ test_debugger.cpp: In function ‘constexpr const int findlastslash()’: test_debugger.cpp:4:19: error: ‘path’ was not declared in this scope if constexpr (path[PathIndex - 1] == '/' || path[PathIndex - 1] == '\') { ^~~~ test_debugger.cpp:8:45: error: ‘PathLength’ was not declared in this scope return findlastslash<PathIndex - 1, PathLength, path>(); ^~~~~~~~~~ test_debugger.cpp:8:45: note: suggested alternative: ‘PathIndex’ return findlastslash<PathIndex - 1, PathLength, path>(); ^~~~~~~~~~ PathIndex test_debugger.cpp: At global scope: test_debugger.cpp:12:29: error: ‘PathLength’ was not declared in this scope template< const char (path)[PathLength] > ^~~~~~~~~~ test_debugger.cpp: In function ‘constexpr const int startfindlastslash()’: test_debugger.cpp:15:27: error: ‘path’ was not declared in this scope return findlastslash< path, PathLength >(); ^~~~ test_debugger.cpp:15:33: error: ‘PathLength’ was not declared in this scope return findlastslash< path, PathLength >(); ^~~~~~~~~~ test_debugger.cpp: In function ‘int main(int, const char**)’: test_debugger.cpp:19:51: error: no matching function for call to ‘startfindlastslash<"c/test">()’ static_assert( startfindlastslash< "c/test" >() == 1, "Fail" ); ^ test_debugger.cpp:13:21: note: candidate: template<<declaration error> > constexpr const int startfindlastslash() constexpr const int startfindlastslash() ^~~~~~~~~~~~~~~~~~ test_debugger.cpp:13:21: note: template argument deduction/substitution failed:
来自
constexpr unsigned int requires_inRange(unsigned int i, unsigned int len) {
return i >= len ? throw i : i;
}
class StrWrap
{
unsigned size_;
const char * begin_;
public:
template< unsigned N >
constexpr StrWrap( const char(&arr)[N] ) : begin_(arr), size_(N - 1) {
static_assert( N >= 1, "not a string literal");
}
constexpr char operator[]( unsigned i ) {
return requires_inRange(i, size_), begin_[i];
}
constexpr operator const char *() {
return begin_;
}
constexpr unsigned size() {
return size_;
}
};
constexpr unsigned count( StrWrap str, char c, unsigned i = 0, unsigned ans = 0 )
{
return i == str.size() ? ans :
str[i] == c ? count(str, c, i + 1, ans + 1) :
count(str, c, i + 1, ans);
}
int main(int argc, char const *argv[])
{
static_assert( count("dude", 'd' ) == 2, "d != 2" );
return 0;
}