C ++连接两个`const char`string文字
使用constexpr
可以连接两个string文字吗? 或者换句话说,可以消除代码中的macros:
#define nl(str) str "\n" int main() { std::cout << nl("usage: foo") nl("print a message") ; return 0; }
更新 :使用"\n"
没有什么错,但是我想知道是否可以使用constexpr
来replace这些types的macros。
-
是的,完全可以创build编译时常量string,并使用constexpr函数甚至操作符来处理它们。 然而,
-
编译器不需要执行静态和线程持续时间对象以外的任何对象的持续初始化。 特别是,临时对象(不是variables,并且具有比自动存储持续时间less的东西)不需要被不断地初始化,就我所知,没有一个编译器可以对数组进行操作。 参见定义常量初始化的3.6.2 / 2-3,以及6.7.4关于块级静态持续variables的更多措辞。 这些都不适用于临时性的,其临终期限定在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,虽然有时候我喜欢在网上玩,所以可能会有一些与上面相矛盾的标准。
(尽pipe有免责声明,并且被@DyP所推动,我还是增加了一些在语言和律师上的引用。)
一点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>{}); }
现场示例。
我会再补充一点,但是我必须在这之前先走下去。 你应该可以从中工作。
乍一看,C ++ 11用户定义的string似乎是一个更简单的方法。 (例如,如果您正在寻找一种在编译时全局启用和禁用换行注入的方法)
不,对于constexpr
你首先需要一个合法的函数,函数不能粘贴string参数等。
如果你考虑一个常规函数中的等价expression式,它将分配内存和串联string – 绝对不适合constexpr
。
- 你不能从函数返回一个(普通)数组。
- 你不能在constexpr中创build一个新的
const char[n]
(§7.1.5/ 3 dcl.constexpr)。 - 一个地址常量expression式必须指向一个静态存储持续时间的对象(§5.19/ 3 expr.const) – 这不允许有一些types的对象组装这个数组用于连接,而你的constexpr只是把它转换成一个ptr 。
- 传递给constexpr的参数不被认为是编译时常量,所以你也可以在运行时使用fct – 这不允许使用模板元编程的一些技巧。
- 你不能把string的单个字符作为模板parameter passing给一个函数 – 这不允许一些其他的模板元编程技巧。
所以(据我所知),你不能得到一个constexpr返回一个新构造的string或一个char const[n]
的char const*
。 注意,这些限制中的大部分都不适用于Xeo指出的std::array
。
即使你可以返回一些char const*
,返回值也不是一个文字,只有相邻的string连接在一起。 这发生在翻译阶段6(§2.2),我仍然会称之为预处理阶段。 Constexpr稍后评估(ref?)。 ( f(x) f(y)
其中f
是函数是语法错误afaik)
但是你可以从你的constexpr函数中返回一个包含两个string的其他types的对象(包含一个或一个集合),并且可以插入/打印到一个basic_ostream
。
编辑:这是例子。 这是相当长的oO注意,你可以简化这个只是为了得到一个额外的“\ n”添加一个string的结尾。 (这更多的是我从记忆中写下来的一种通用的方法。)
编辑2:其实,你不能真正简化它。 使用包含'\ n'(而不是string文字数组)的arr
数据成员创buildarr
数据成员使用一些花哨的可变参数模板代码实际上是更长的时间(但它的工作原理,请参阅Xeo的答案)。
注意:由于ct_string_vector
(名称不好)存储指针,因此它只能用于静态存储持续时间的string(如文字或全局variables)。 好处是string不必被模板机制复制和扩展。 如果你使用一个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; }