问题
This code task a const char[]
and finds where is the last slash:
#include <array>
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template< int PathIndex, int PathLength >
constexpr const int findlastslash(const char (&path)[PathLength])
{
constexpr const int end = PathLength - PathIndex;
return (PathIndex >= 0 && path[end] != '/' && path[end] != '\\')
? findlastslash< PathIndex - 1, PathLength >( path ) : ( end + 1 );
}
template< int PathLength >
constexpr const int startfindlastslash(const char (&path)[PathLength]) {
return findlastslash< PathLength >( path );
}
int main(int argc, char const *argv[])
{
STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11 );
}
But it does not work because the template recursion never stops:
$ g++ -o main.exe --std=c++14 test_debugger.cpp
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:2:28: error: static assertion failed: startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 17
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
^
test_debugger.cpp:18:5: note: in expansion of macro ‘STATIC_ASSERT’
STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 17 );
^~~~~~~~~~~~~
test_debugger.cpp: In instantiation of ‘constexpr const int findlastslash(const char (&)[PathLength]) [with int PathIndex = -880; int PathLength = 30]’:
test_debugger.cpp:8:114: recursively required from ‘constexpr const int findlastslash(const char (&)[PathLength]) [with int PathIndex = 19; int PathLength = 30]’
test_debugger.cpp:8:114: required from here
test_debugger.cpp:8:114: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
return (PathIndex >= 0 && path[end] != '/' && path[end] != '\\') ? findlastslash< PathIndex - 1, PathLength >( path ) : ( end + 1 );
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
compilation terminated.
I know can do this easily using const char *
, but I am interested in keeping using string "cppdebugger/test_debugger.cpp"
(const char[]
) as a array, i..e, without decaying into a const char *
pointer.
Update
Correction. A template specialization is like this (with < 1, PathLength >
:
template< int PathLength >
constexpr const int findlastslash< 1, PathLength >(const char (&path)[PathLength])
{
constexpr const int end = PathLength;
return ( path[end] != '/' && path[end] != '\\') ? 0 : 1;
}
Where the compiler correctly complains it:
$ g++ -o main.exe --std=c++14 test_debugger.cpp
test_debugger.cpp:18:82: error: non-class, non-variable partial specialization ‘findlastslash<1, PathLength>’ is not allowed
constexpr const int findlastslash< 1, PathLength >(const char (&path)[PathLength])
^
test_debugger.cpp: In function ‘int main(int, const char**)’:
test_debugger.cpp:2:28: error: static assertion failed: startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
^
test_debugger.cpp:26:5: note: in expansion of macro ‘STATIC_ASSERT’
STATIC_ASSERT( startfindlastslash( "cppdebugger/test_debugger.cpp" ) == 11 );
^~~~~~~~~~~~~
test_debugger.cpp: In instantiation of ‘constexpr const int findlastslash(const char (&)[PathLength]) [with int PathIndex = -880; int PathLength = 30]’:
test_debugger.cpp:9:56: recursively required from ‘constexpr const int findlastslash(const char (&)[PathLength]) [with int PathIndex = 19; int PathLength = 30]’
test_debugger.cpp:9:56: required from here
test_debugger.cpp:9:56: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum)
? findlastslash< PathIndex - 1, PathLength >( path ) : ( end + 1 );
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~
compilation terminated.
Related questions:
- Understanding (simple?) C++ Partial Template Specialization
- Do I need to put constexpr after else-if?
- Equivalent ternary operator for constexpr if?
回答1:
If you are in C++14, you no longer require recursion for constexpr
, you can use regular loop:
template<std::size_t PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
for (std::size_t i = PathLength; i != 0; --i) {
if (path[i - 1] == '/' || path[i - 1] == '\\') {
return i;
}
}
return 0;
}
In C++11, you might do:
template<std::size_t PathLength>
constexpr const int findlastslash(const char (&path)[PathLength], std::size_t i = PathLength)
{
return (i == 0)
? 0
: ((path[i - 1] == '/' || path[i - 1] == '\\')
? i
: findlastslash(path, i - 1));
}
constexpr version in C++17 (to get rid of regular parameter i
):
template<std::size_t PathLength, std::size_t 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);
}
}
Prior C++17, you have to use specialization or overload instead of if constexpr
, but function cannot be partial specialized.
So struct specialization might be:
template <std::size_t I> struct findlastslash_impl
{
template <std::size_t PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
if (path[I - 1] == '/' || path[I - 1] == '\\') {
return I;
}
return findlastslash_impl<I - 1>()(path);
}
}
template<> struct findlastslash_impl<0>
{
template<std::size_t PathLength>
constexpr const int findlastslash(const char (&)[PathLength])
{
return 0;
}
};
template<std::size_t PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
return findlastslash_impl<PathLength>()(path);
}
or with overloads:
template <std::size_t PathLength>
constexpr const int findlastslash_impl(std::integral_constant<std::size_t, 0>,
const char (&path)[PathLength])
{
return 0;
}
template <std::size_t PathLength, std::size_t I>
constexpr const int findlastslash_impl(std::integral_constant<std::size_t, I>,
const char (&path)[PathLength])
{
if (path[I - 1] == '/' || path[I - 1] == '\\') {
return I;
}
return findlastslash_impl(std::integral_constant<std::size_t, I - 1>(), path);
}
template<std::size_t PathLength>
constexpr const int findlastslash(const char (&path)[PathLength])
{
return findlastslash_impl(std::integral_constant<std::size_t, PathLength>(), path);
}
回答2:
Note that this functionality is already available in C++17 in std::string_view.
See: std::basic_string_view::rfind
#include <string_view>
using namespace std::literals;
int main()
{
static_assert("cppdebugger/test_debugger.cpp"sv.rfind('/') == 11);
static_assert("cppdebugger/test_debugger.cpp"sv.find_last_of("\\/"sv) == 11);
}
Live demo
回答3:
I managed to fix it by using class partial template specialization. I did not use function partial template specialization because it is not allowed: Why I cannot partially specialize template free functions, only classes?
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__)
template<int PathIndex>
struct Findlastslash
{
template< int PathLength >
static constexpr int start(const char (&path)[PathLength])
{
constexpr const int end = PathLength - PathIndex;
return (PathIndex >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash< PathIndex - 1 >::start( path ) : ( end );
}
};
template<>
struct Findlastslash< 1 >
{
template< int PathLength >
static constexpr int start(const char (&path)[PathLength]) {
return 0;
}
};
template< int PathLength >
constexpr const int startfindlastslash(const char (&path)[PathLength]) {
return Findlastslash< PathLength >::start( path );
}
int main(int argc, char const *argv[])
{
STATIC_ASSERT( startfindlastslash( "h/bye" ) == 1 );
}
This is the expanded template:
clang++ -Xclang -ast-print -fsyntax-only --std=c++14 test_debugger.cpp > main.exe
template <int PathIndex>
struct Findlastslash
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength])
{
constexpr int end = PathLength - PathIndex;
return (PathIndex >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<PathIndex - 1>::start(path) : (end);
}
};
template<>
struct Findlastslash<6>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]);
template<> static constexpr int start<6>(const char (&path)[6])
{
constexpr int end = 6 - 6;
return (6 >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<6 - 1>::start(path) : (end);
};
};
template<>
struct Findlastslash<5>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]);
template<> static constexpr int start<6>(const char (&path)[6])
{
constexpr int end = 6 - 5;
return (5 >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<5 - 1>::start(path) : (end);
};
};
template<>
struct Findlastslash<4>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]);
template<> static constexpr int start<6>(const char (&path)[6])
{
constexpr int end = 6 - 4;
return (4 >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<4 - 1>::start(path) : (end);
};
};
template<>
struct Findlastslash<3>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]);
template<> static constexpr int start<6>(const char (&path)[6])
{
constexpr int end = 6 - 3;
return (3 >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<3 - 1>::start(path) : (end);
};
};
template<>
struct Findlastslash<2>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]);
template<> static constexpr int start<6>(const char (&path)[6])
{
constexpr int end = 6 - 2;
return (2 >= 0 && path[end] != '/' && path[end] != '\\')
? Findlastslash<2 - 1>::start(path) : (end);
};
};
template<>
struct Findlastslash<1>
{
template <int PathLength>
static constexpr int start(const char (&path)[PathLength]) {
return 0;
}
template<>
static constexpr int start<6>(const char (&path)[6]) {
return 0;
}
};
template <int PathLength>
constexpr const int startfindlastslash(const char (&path)[PathLength]) {
return Findlastslash<PathLength>::start(path);
}
template<>
constexpr const int startfindlastslash<6>(const char (&path)[6]) {
return Findlastslash<6>::start(path);
}
int main(int argc, const char *argv[]) {
static_assert(startfindlastslash("h/bye")==1, "startfindlastslash( \"h/bye\" )==1");
}
来源:https://stackoverflow.com/questions/59472892/how-to-stop-template-recursion-while-using-parameter-deduction