我看了你的问题,发现没有人真正回答。我花了一整天的时间来解决问题,但无论如何我的工作都需要它。
我的工作需要constexpr,我就是带着这个想法写了答案。你的工作没有具体说明,但它不会受到伤害。
什么没用;即我的第一次尝试
(我在一台使用了 10 年的 PowerPC Mac 上使用来自 MacPorts 的 GCC-4.7。)
您可以轻松地将 (C++11) 可变参数函数参数列表转换为任何类型的元组:
template < typename Destination, typename ...Source >
constexpr
auto initialize( Source&& ...args ) -> Destination
{ return Destination{ce_forward<Source>(args)...}; }
(ce_forward 函数模板和std::forward 一样,只是我明确地将它设为constexpr。)
(当我没有将Destination 放在正文中时,我的编译器给了我与无法使用std::initialization_list 初始化的目的地相关的错误;所以我现在拥有的表单应该适用于任何聚合或目标类型支持的构造函数。)
但我们需要先走另一条路,然后使用我上面的代码翻译回来。我试过这样的代码:
template < typename Destination, typename Source, typename Size1, typename Size2, typename ...Args >
constexpr
auto fill_from_array( Source&& source, Size1 index_begin, Size2 index_end, Args&& ...args )
-> Destination
{
return ( index_begin < index_end )
? fill_from_array<Destination>( ce_forward<Source>(source), index_begin + 1, index_end, ce_forward<Args>(args)..., ce_forward<Source>(source)[index_begin] )
: initialize<Destination>( ce_forward<Args>(args)... );
}
(因为我也需要两个来源,所以我制作了这个函数的更大版本。)
当我实际运行此代码时,我的计算机在一个小时后因超出虚拟内存而崩溃。我猜这会导致无限循环或其他东西。 (或者它可能是有限的,但对于我的古老系统来说太多了。)
我的第二次尝试
我搜索了 S.O.直到我找到有用的东西:
并从我读到的这些和其他内容中拼凑出一个解决方案:Parsing strings at compile-time — Part I。基本上,我们使用上面我的initialize 函数模板的变体;我们不是完全基于函数可变参数来构造初始化器,而是使用模板可变参数的映射。
最简单的映射源是非负整数:
#include <cstddef>
template < std::size_t ...Indices >
struct index_tuple
{ using next = index_tuple<Indices..., sizeof...(Indices)>; };
template < std::size_t Size >
struct build_indices
{ using type = typename build_indices<Size - 1>::type::next; };
template < >
struct build_indices< 0 >
{ using type = index_tuple<>; };
index_tuple 类模板是我们将为映射传递的,而build_indices 类模板将index_tuple 实例化以正确的格式:
index_tuple<>
index_tuple<0>
index_tuple<0, 1>
index_tuple<0, 1, 2>
...
我们用函数模板创建index_tuple对象:
template < std::size_t Size >
constexpr
auto make_indices() noexcept -> typename build_indices<Size>::type
{ return {}; }
我们使用上述 index_tuple 对象来贡献函数模板的模板头:
#include <array>
template < std::size_t N, std::size_t M, std::size_t ...Indices >
constexpr
std::array<char, N + M - 1u>
fuse_strings_impl( const char (&f)[N], const char (&s)[M], index_tuple<Indices...> );
第三个参数没有名字,因为我们不需要对象本身。我们只需要标题中的“std::size_t ...Indices”。我们知道展开后会变成“0, 1, ..., X”。我们将把这种有序的扩展提供给一个函数调用,该函数调用被扩展为所需的初始化器。举个例子,我们看一下上面函数的定义:
template < std::size_t N, std::size_t M, std::size_t ...Indices >
constexpr
std::array<char, N + M - 1u>
fuse_strings_impl( const char (&f)[N], const char (&s)[M], index_tuple<Indices...> )
{ return {{ get_strchr<Indices>(f, s)... }}; }
我们将返回一个array,其中第一个元素为get_strchr<0>(f,s),第二个元素为get_strchr<1>(f,s),以此类推。请注意,此函数名称以“_impl”结尾,因为我隐藏了 index_tuple 的使用并通过调用公共版本来确保正确的基本情况:
template < std::size_t N, std::size_t M >
constexpr
std::array<char, N + M - 1u>
fuse_strings( const char (&f)[N], const char (&s)[M] )
{ return fuse_strings_impl(f, s, make_indices<N + M - 2>()); }
你可以尝试这样编码:
#include <iostream>
#include <ostream>
int main()
{
using std::cout;
using std::endl;
constexpr auto initialize_test = initialize<std::array<char, 15>>( 'G',
'o', 'o', 'd', 'b', 'y', 'e', ',', ' ', 'm', 'o', 'o', 'n', '!', '\0' );
constexpr char hello_str[] = "Hello ";
constexpr char world_str[] = "world!";
constexpr auto hw = fuse_strings( hello_str, world_str );
cout << initialize_test.data() << endl;
cout << hw.data() << endl;
}
有一些微妙之处需要注意。
- 您的
const(expr) char str[] = "Whatever"; 声明必须使用[] 而不是*,因此编译器将您的对象识别为内置数组,而不是指向运行时长度未知(固定)内存的指针。
- 由于内置数组不能用作返回类型,您必须使用
std::array 作为替代。问题是当您必须将结果用于以后的合并时。 std::array 对象必须具有适当的内置数组类型的公开可用的非静态数据成员,但该成员的名称未在标准中指定并且可能不一致。所以你必须创建一个std::array work-alike 来正式避免黑客攻击。
- 一般数组连接和字符串连接不能使用相同的代码。字符串是
char 数组,其中最后一个元素必须是'\0'。从字符串读取时必须跳过那些NUL 值,但在写入时添加。这就是为什么奇怪的 1 和 2 出现在我的代码中。对于一般数组连接(包括非字符串 char 连接),必须读取每个数组中的每个元素,并且不应将额外元素添加到组合数组中。
哦,这是get_strchr的定义:
template < std::size_t N >
constexpr
char ce_strchr( std::size_t i, const char (&s)[N] )
{
static_assert( N, "empty string" );
return (i < ( N - 1 )) ? s[i] : throw "too big";
}
template < std::size_t N, std::size_t M, std::size_t ...L >
constexpr
char ce_strchr( std::size_t i, const char (&f)[N], const char (&s)[M], const char (&...t)[L] )
{
static_assert( N, "empty string" );
return (i < ( N - 1 )) ? f[i] : ce_strchr(i + 1 - N, s, t...);
}
template < std::size_t I, std::size_t N, std::size_t ...M >
constexpr
char get_strchr( const char (&f)[N], const char (&...s)[M] )
{ return ce_strchr(I, f, s...); }
(希望你能读到这篇文章。)