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

4dc9hkyq  于 2023-03-14  发布在  其他
关注(0)|答案(4)|浏览(213)

在“现代C++”(C17或更高版本)中是否可以将字符串作为参数传递给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() {

}
ej83mcc0

ej83mcc01#

是的,在c++20中。
问题是确定模板非类型参数的唯一性很困难。
c++20添加<=>飞船运算符比较。如果它不是用户提供的(并且仅基于非用户提供的<=>,依次递归重复)(并且是few other requirements; see p0732),则该类型可以用作非类型模板参数。
此类类型可以在constexpr构造函数中从原始"strings"构造,包括使用c++17推导指南使其自动调整大小。
由于存储的数据大小可能是类型的一部分,因此您将希望将类型作为auto typed non-type parameter或其他自动推导的类型。
注意,将模板的实现放在一个cpp文件中通常不是一个好主意,但这是另一个问题。

g6ll5ycj

g6ll5ycj2#

在您获得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;
zqdjd7g9

zqdjd7g93#

这比直接传递一个字符串常量更难看,但在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;
}

编译器资源管理器示例here

ryoqjall

ryoqjall4#

这可以在C++17中实现,但是在传递name之前,需要为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("beans\n")> beans {"beans\n"};
    xyz<beans> test{};
    std::cout << test.get_name();
    
    static constexpr util::c_str<std::size("beans\n")> beans_two {"beans\n"};
    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';
}

相关问题