c++ - 使用参数推导时如何停止模板递归?

标签 c++ arrays templates c++14 c++17

此代码任务 const 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 * 轻松做到这一点,但我有兴趣继续使用字符串 "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. > Understanding (simple?) C++ Partial Template Specialization
  2. > Do I need to put constexpr after else-if?
  3. > Equivalent ternary operator for constexpr if?

最佳答案

如果你在 C++14 中,你不再需要 constexpr 的递归,你可以使用常规循环:

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

在 C++11 中,你可能会这样做:

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

C++17 中的 constexpr 版本(去除常规参数 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);
    }
}

在 C++17 之前,您必须使用特化或重载来代替 if constexpr,但函数不能部分特化。

所以结构特化可能是:

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

或重载:

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

关于c++ - 使用参数推导时如何停止模板递归?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59472892/

相关文章:

c++ - 我可以在 Xcode 的单个应用程序中混合使用 Objective-C 和 C++ 吗?

c++ - 类继承——堆叠方法

ruby-on-rails - 从数组计算时间并转换为时区

java - 如何在同一行打印数组和直方图?

具有继承的 C++ 模板部分特化

c++ - nvcc、gcc、clang 和 msvc "respect"在结构中使用 __restrict__ 关键字吗?

c++ - InitUndistortRectifyMap 和 Remap

ios - Swift 中合并子数组

c++ - 如何将枚举模板映射到类型模板?

c++ - vector 中的移动元素未按预期工作