是否可以使用 a 连接两个字符串文字constexpr
?或者换一种说法,可以消除代码中的宏,例如:
#define nl(str) str "\n"
int main()
{
std::cout <<
nl("usage: foo")
nl("print a message")
;
return 0;
}
更新: using 没有任何问题"\n"
,但是我想知道是否可以使用constexpr
来替换那些类型的宏。
是否可以使用 a 连接两个字符串文字constexpr
?或者换一种说法,可以消除代码中的宏,例如:
#define nl(str) str "\n"
int main()
{
std::cout <<
nl("usage: foo")
nl("print a message")
;
return 0;
}
更新: using 没有任何问题"\n"
,但是我想知道是否可以使用constexpr
来替换那些类型的宏。
一点点constexpr
,撒上一些 TMP 和指数的顶部给了我这个:
#include <array>
template<unsigned... Is> struct seq{};
template<unsigned N, unsigned... Is>
struct gen_seq : gen_seq<N-1, N-1, Is...>{};
template<unsigned... Is>
struct gen_seq<0, Is...> : seq<Is...>{};
template<unsigned N1, unsigned... I1, unsigned N2, unsigned... I2>
constexpr std::array<char const, N1+N2-1> concat(char const (&a1)[N1], char const (&a2)[N2], seq<I1...>, seq<I2...>){
return {{ a1[I1]..., a2[I2]... }};
}
template<unsigned N1, unsigned N2>
constexpr std::array<char const, N1+N2-1> concat(char const (&a1)[N1], char const (&a2)[N2]){
return concat(a1, a2, gen_seq<N1-1>{}, gen_seq<N2>{});
}
我会再充实一些,但我必须开始并想在那之前把它放下。你应该能够从中工作。
const char[n]
在 constexpr (§7.1.5/3 dcl.constexpr) 中创建新的。所以(据我所知),你不能得到一个 constexpr 返回char const*
一个新构造的字符串的 a 或 a char const[n]
。请注意,这些限制中的大多数并不适用于std::array
Xeo 所指出的。
即使您可以返回 some char const*
,返回值也不是文字,并且只有相邻的字符串文字被连接起来。这发生在翻译阶段 6(第 2.2 节)中,我仍将其称为预处理阶段。Consexpr 稍后评估(参考?)。(函数f(x) f(y)
在哪里f
是语法错误afaik)
但是您可以从您的 constexpr fct 返回包含两个字符串并且可以插入/打印到basic_ostream
.
编辑:这是示例。它有点长 oO 请注意,您可以简化它,以便在字符串末尾添加一个额外的“\n”。(这更像是我刚刚从记忆中写下来的通用方法。)
Edit2:实际上,您不能真正简化它。将数据成员创建arr
为包含 '\n' 的“const char_type 数组”(而不是字符串文字数组)使用一些花哨的可变参数模板代码,这些代码实际上有点长(但它有效,请参阅 Xeo 的答案)。
注意:由于ct_string_vector
(名称不好)存储指针,它应该只与静态存储持续时间的字符串(例如文字或全局变量)一起使用。优点是不必通过模板机制复制和扩展字符串。如果您使用 constexpr 来存储结果(如示例中main
),如果传递的参数不是静态存储持续时间,您的编译器应该抱怨。
#include <cstddef>
#include <iostream>
#include <iterator>
template < typename T_Char, std::size_t t_len >
struct ct_string_vector
{
using char_type = T_Char;
using stringl_type = char_type const*;
private:
stringl_type arr[t_len];
public:
template < typename... TP >
constexpr ct_string_vector(TP... pp)
: arr{pp...}
{}
constexpr std::size_t length()
{ return t_len; }
template < typename T_Traits >
friend
std::basic_ostream < char_type, T_Traits >&
operator <<(std::basic_ostream < char_type, T_Traits >& o,
ct_string_vector const& p)
{
std::copy( std::begin(p.arr), std::end(p.arr),
std::ostream_iterator<stringl_type>(o) );
return o;
}
};
template < typename T_String >
using get_char_type =
typename std::remove_const <
typename std::remove_pointer <
typename std::remove_reference <
typename std::remove_extent <
T_String
> :: type > :: type > :: type > :: type;
template < typename T_String, typename... TP >
constexpr
ct_string_vector < get_char_type<T_String>, 1+sizeof...(TP) >
make_ct_string_vector( T_String p, TP... pp )
{
// can add an "\n" at the end of the {...}
// but then have to change to 2+sizeof above
return {p, pp...};
}
// better version of adding an '\n':
template < typename T_String, typename... TP >
constexpr auto
add_newline( T_String p, TP... pp )
-> decltype( make_ct_string_vector(p, pp..., "\n") )
{
return make_ct_string_vector(p, pp..., "\n");
}
int main()
{
// ??? (still confused about requirements of constant init, sry)
static constexpr auto assembled = make_ct_string_vector("hello ", "world");
enum{ dummy = assembled.length() }; // enforce compile-time evaluation
std::cout << assembled << std::endl;
std::cout << add_newline("first line") << "second line" << std::endl;
}
是的,完全可以创建编译时常量字符串,并使用 constexpr 函数甚至运算符来操作它们。然而,
除了静态和线程持续时间对象之外,编译器不需要执行任何对象的常量初始化。特别是,临时对象(不是变量,并且具有小于自动存储持续时间的东西)不需要进行常量初始化,据我所知,没有编译器对数组这样做。参见 3.6.2/2-3,它定义了常量初始化,以及 6.7.4 中关于块级静态持续时间变量的更多措辞。这些都不适用于临时人员,其生命周期在 12.2/3 及以下版本中定义。
因此,您可以通过以下方式实现所需的编译时连接:
static const auto conc = <some clever constexpr thingy>;
std::cout << conc;
但你不能让它工作:
std::cout << <some clever constexpr thingy>;
更新:
但是你可以让它工作:
std::cout << *[]()-> const {
static constexpr auto s = /* constexpr call */;
return &s;}()
<< " some more text";
但是样板标点符号太丑陋了,不能使它成为一个有趣的小技巧。
(免责声明:IANALL,虽然有时我喜欢在互联网上玩一个。所以标准中可能有一些尘土飞扬的角落与上述内容相矛盾。)
(尽管有免责声明,并由@DyP 推动,我添加了更多的语言律师引用。)
乍一看,C++11 用户定义的字符串文字似乎是一种更简单的方法。(例如,如果您正在寻找一种在编译时全局启用和禁用换行注入的方法)
不,因为constexpr
您首先需要一个合法的函数,而函数不能粘贴字符串文字参数等。
如果您考虑常规函数中的等效表达式,它将分配内存并连接字符串 - 绝对不适合constexpr
.