在现代C++中是否可以将字符串文本作为参数传递给C++模板?

Is it possible in modern C++ to pass a string literal as a parameter to a C++ template?

本文关键字:C++ 文本 参数传递 模板 字符串 是否      更新时间:2023-10-16

是否可以在"现代C++"(C++17 或更高版本(中将字符串文本作为参数传递给C++模板?

我意识到你可以用构造函数参数来做到这一点;我只是认为将其作为模板参数会更方便,而不是深埋在 cpp 文件中。我很好奇这是否是现代C++的新功能。请参阅下面的伪代码,了解我正在尝试执行的操作:

伪代码示例:

// Header File /////////////////////////
template<constexpr string Name>
class ModuleBase {
public:
ModuleBase();
string name;
};
class xyz : ModuleBase<"xyz"> {
public:
xyz();
};
// Cpp File //////////////////////////
template<string_literal Name>
ModuleBase<Name>::ModuleBase() {
name = Name;
}
xyz::xyz() : ModuleBase() {
}

是的,在 c++20 中。

问题是确定模板非类型参数的唯一性很困难。

C++20增加了<=>宇宙飞船操作员比较。 如果它是非用户提供的(并且仅基于非用户提供的<=>依次递归重复((以及其他一些要求;请参阅 p0732(,则该类型可以用作非类型模板参数。

此类类型可以从constexpr构造函数中的原始"strings"构造,包括使用 c++17 演绎指南使它们自动调整大小。

由于存储的数据的大小可能是类型的一部分,因此您需要将该类型作为auto类型化非类型参数或其他自动推断的类型。


请注意,将模板的实现放在 cpp 文件中通常是一个坏主意。 但这是另一个问题。

在你得到 c++20 并且如果你有 boost,你可能会发现以下宏很有用:

#define C_STR(str_) boost::mpl::c_str< BOOST_METAPARSE_STRING(str_) >::value

然后按如下方式使用:

template<const char* str>
structe testit{
};
testit<C_STR("hello")> ti;

这比直接传递字符串文字更丑陋,但在 C++20 中,也可以使用 NTTP constexpr lambda 返回字符串:

#include <string_view>
template<auto getStrLambda>
struct MyType {
static constexpr std::string_view myString{getStrLambda()};
};
int main() {
using TypeWithString = MyType<[]{return "Hello world!";}>;
return 0;
}

此处的编译器资源管理器示例。

它可以在 C++17 中完成,但在将 C 字符串作为名称传递之前,您需要static constexpr

存储:https://godbolt.org/z/ojnv8xPca
#include<cstddef>
#include<string> //std::size()
namespace util {
template<std::size_t N>
using c_str = char const [N];
template<auto const & str, std::size_t iter = std::size(str), char const ... chars>
struct c_str_to_arg_pack {
using arg_pack_t = 
typename c_str_to_arg_pack<str, iter-1, str[iter-1], chars ... >
::arg_pack_t;
};
template<auto const & str, char const ... chars>
struct c_str_to_arg_pack<str, 0, chars ...> {
using arg_pack_t = c_str_to_arg_pack<str, 0, chars...>;
template <template <char ...> typename T>
using apply = T<chars ...>;
};
template<auto const & str, char const ... chars>
using c_str_to_arg_pack_t = typename c_str_to_arg_pack<str, chars ... >::arg_pack_t;
}//end util
#include<string_view>
#include<array>
template <char ... chars>
struct module_base {
std::string const & get_name() {
static std::string name = []()
{
std::string name;
name.reserve(sizeof...(chars));
(name += ... += chars); 
return name;
}();
return name;
};
static constexpr std::array<char, sizeof ... (chars)> name_backing {chars ...};
static constexpr std::string_view constexpr_name {name_backing.begin(), name_backing.size() };
static int instance_count;
module_base (){
++instance_count;
}
virtual ~module_base() = default;
};
template <char ... chars>
int module_base<chars ...>::instance_count {};
template <auto const & name>
struct xyz : util::c_str_to_arg_pack_t<name>::template apply<module_base> //edit, formerly missing 'template'
{};
template <auto const & name>
struct abc : util::c_str_to_arg_pack_t<name>::template apply<module_base> //edit, formerly missing 'template'
{};
#include<iostream>
int main(){
static constexpr util::c_str<std::size("beansn")> beans {"beansn"};
xyz<beans> test{};
std::cout << test.get_name();

static constexpr util::c_str<std::size("beansn")> beans_two {"beansn"};
abc<beans_two> test_two{};
std::cout << test_two.constexpr_name;

std::cout << decltype(test)::instance_count << 'n';
std::cout << decltype(test_two)::instance_count << 'n';
}