首页 > 解决方案 > 使用参数推导时如何停止模板递归?

问题描述

此代码任务 aconst char[]并查找最后一个斜杠在哪里:

#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 );
}

但它不起作用,因为模板递归永远不会停止:

$ 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.

我知道使用 可以轻松做到这一点const char *,但我有兴趣继续使用 string "cppdebugger/test_debugger.cpp"( const char[]) 作为数组,即,不会衰减为const char *指针。


更新

更正。模板专业化是这样的(使用< 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;
}

编译器正确抱怨的地方:

$ 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.

相关问题:

  1. 理解(简单?)C++ 部分模板专业化
  2. 我需要把 constexpr 放在 else-if 之后吗?
  3. constexpr if 的等效三元运算符?

标签: c++arraystemplatesc++14c++17

解决方案


请注意,此功能已在 C++17 中的std::string_view中可用。

见: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);
}

现场演示


推荐阅读