是否可以使用constexpr
连接两个字符串文字?换句话说,可以消除代码中的宏,如:
#define nl(str) str "\n"
int main()
{
std::cout <<
nl("usage: foo")
nl("print a message")
;
return 0;
}
更新:使用"\n"
没有任何问题,但我想知道是否可以使用constexpr
替换这些类型的宏。
答案 0 :(得分:13)
一点点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>{});
}
我会更多地充实它,但我必须开始并想在此之前放弃它。你应该可以从中工作。
答案 1 :(得分:1)
乍一看,C ++ 11用户定义的字符串文字似乎是一种更简单的方法。 (例如,如果您正在寻找一种在编译时全局启用和禁用换行注入的方法)
答案 2 :(得分:1)
const char[n]
(§7.1.5/ 3 dcl.constexpr)。所以(据我所知),你不能得到一个返回char const*
新构造的字符串或char const[n]
的constexpr。请注意,大多数这些限制不适用于Xeo指出的std::array
。
即使你可以返回一些char const*
,返回值也不是文字,只连接相邻的字符串文字。这发生在翻译阶段6(§2.2),我仍称之为预处理阶段。 Constexpr稍后进行评估(参考?)。 (f(x) f(y)
其中f
是函数是语法错误afaik)
但是你可以从constexpr fct返回一个其他类型的对象(有一个constexpr ctor或者是一个聚合),它包含两个字符串,可以插入/打印到basic_ostream
。
编辑:这是一个例子。这是相当长的o.O. 请注意,您可以简化此操作,以获得额外的“\ n”添加字符串的结尾。 (这更像是我刚从记忆中写下的一般方法。)
Edit2:实际上,你无法真正简化它。创建arr
数据成员作为“const char_type数组”并包含'\ n'(而不是字符串文字数组)使用一些花哨的可变参数模板代码,实际上有点长(但它有效,请参阅Xeo的回答)。
注意:由于ct_string_vector
(名称不好)存储指针,因此它应仅用于静态存储持续时间的字符串(例如文字或全局变量)。优点是不必复制字符串&amp;通过模板机制扩展。如果使用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;
}
答案 3 :(得分:1)
是的,完全可以创建编译时常量字符串,并使用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推动,我还增加了一些语言律师引用。)
答案 4 :(得分:0)
不,对于constexpr
,你首先需要一个合法的函数,函数不能粘贴字符串文字参数等。
如果你考虑常规函数中的等价表达式,它将分配内存并连接字符串 - 绝对不适合constexpr
。