【发布时间】:2019-12-26 19:08:06
【问题描述】:
我有这个 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!" );
}
我想停止写/硬编码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!" );
}
当然,上面的代码是完全无效的。但是,它是描述事物的简单方法的一个很好的近似值。
你会如何解决这个问题?您会将 constexpr const char path[7] = "c/test"; 替换为 std::array 或 std::string_view 吗?
我尝试使用 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.cpptest_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.exetest_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? 上,我发现这个示例代码使用 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 类吗? C++ STL(标准模板库)没有任何东西可以用来代替这个常见/简单的任务?
作为参考,我找到了这个不错的相关第三方库:
-
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.
【问题讨论】:
-
请不要再说“constexpr 模板参数”——没有其他类型的。
-
@DavisHerring,这是一个
pleonasm来表示同情并使其非常明显。如果我只是说template parameters,那么有人可以考虑一下:template<int Size> function(const char (&name)[Size])而不是template<const char (name)[Size]> function()之类的东西。但正如我添加的,它必须是constexpr,不可能是前者,因为函数参数不能是constexpr。 -
对于给定的示例,
template< std::sise_t N> constexpr int find_last_slash(const char (&s)[N])可以完成这项工作。 (以及 C++20 中的consteval以避免可能的运行时调用)。 -
@user: "如果我只是说模板参数,有人可能会想到:template
function(const char (&name)[Size]) 而不是 template " 他们认为这是错误的,因为function(). name不是模板参数。这就是为什么“模板参数”和“函数参数”有区别的原因。 -
@Jarod42,如果我理解正确,您在最初编写该示例时已经这样做了,但是无法使用我添加的修改,即使用
const char(&s)[N]直接在 constexpr 中。
标签: c++ arrays templates c++17 c++20