C ++ concat два строковых литерала `const char`

Можно ли конкатцировать два строковых литерала с помощью constexpr ? Или перефразируйте, можно ли исключить macros в коде, например:

 #define nl(str) str "\n" int main() { std::cout << nl("usage: foo") nl("print a message") ; return 0; } 

Обновление . Нет ничего плохого в использовании "\n" , однако я хотел бы знать, можно ли использовать constexpr для замены этих типов макросов.

  1. Да, вполне возможно создать константные строки времени компиляции и манипулировать ими с помощью функций constexpr и даже операторов. Однако,

  2. Компилятор не обязан выполнять постоянную инициализацию любого объекта, кроме объектов статической и длительной продолжительности. В частности, временные объекты (которые не являются переменными и имеют что-то меньшее, чем продолжительность автоматического хранения) не требуются для постоянной инициализации, и насколько я знаю, компилятор не делает это для массивов. См. 3.6.2 / 2-3, которые определяют постоянную инициализацию, и 6.7.4 для некоторой дополнительной формулировки относительно переменных статической продолжительности уровня блока. Ни один из них не применяется к временным ресурсам, срок жизни которых определен в 12.2 / 3 и ниже.

Таким образом, вы можете достичь желаемой конкатенации времени компиляции с помощью:

 static const auto conc = ; std::cout << conc; 

но вы не можете заставить его работать:

 std::cout << ; 

Обновить:

Но вы можете заставить его работать:

 std::cout << *[]()-> const { static constexpr auto s = /* constexpr call */; return &s;}() << " some more text"; 

Но пунктуация шаблона слишком уродлива, чтобы сделать ее более интересной, чем небольшая взлома.


(Отказ от ответственности: IANALL, хотя иногда мне нравится играть в интернете, поэтому могут быть некоторые пыльные углы стандарта, что противоречит вышеизложенному.)

(Несмотря на отказ от ответственности и нажатие на @DyP, я добавил еще несколько цитат из языка).

Немного constexpr , посыпанный некоторым TMP и верхним constexpr , дает мне следующее:

 #include  template struct seq{}; template struct gen_seq : gen_seq{}; template struct gen_seq<0, Is...> : seq{}; template constexpr std::array concat(char const (&a1)[N1], char const (&a2)[N2], seq, seq){ return {{ a1[I1]..., a2[I2]... }}; } template constexpr std::array concat(char const (&a1)[N1], char const (&a2)[N2]){ return concat(a1, a2, gen_seq{}, gen_seq{}); } 

Живой пример.

Я бы добавил, что это еще что-то, но я должен идти и хотел отказаться от него. Вы должны уметь работать от этого.

На первый взгляд, пользовательские строковые литералы C ++ 11 выглядят намного проще. (Если, например, вы ищете способ глобально включить и отключить ввод новой строки во время компиляции)

  • Вы не можете вернуть (простой) массив из функции.
  • Вы не можете создать новый const char[n] внутри constexpr (§7.1.5 / 3 dcl.constexpr).
  • Выражение константы адреса должно относиться к объекту статической продолжительности хранения (§5.19 / 3 expr.const) – это запрещает некоторые трюки с объектами типов, имеющих constexpr ctor, собирающую массив для конкатенации, и ваш constexpr fct просто преобразует его в ptr ,
  • Аргументы, переданные constexpr, не считаются константами времени компиляции, поэтому вы также можете использовать fct во время выполнения – это запрещает некоторые трюки с метапрограммированием шаблонов.
  • Вы не можете получить одиночный символ строкового литерала, переданного функции как аргументы шаблона – это запрещает другие трюки метапрограммирования шаблонов.

Итак, насколько я знаю, вы не можете получить constexpr, возвращающий char const* новой строки или char const[n] . Обратите внимание, что большинство этих ограничений не сохраняются для std::array как указано Xeo.

И даже если вы можете вернуть некоторый char const* , возвращаемое значение не является литералом, и только смежные строковые литералы объединяются. Это происходит в фазе перевода 6 (п. 2.2), которую я бы назвал фазой предварительной обработки. Constexpr оцениваются позже (ref?). ( f(x) f(y) где f – функция, является синтаксической ошибкой afaik)

Но вы можете вернуться из своего constexpr fct объекта какого-либо другого типа (с constexpr ctor или который является агрегатом), который содержит обе строки, и может быть вставлен / напечатан в basic_ostream .


Изменить: вот пример. Это довольно долго oO Примечание, вы можете упорядочить это, чтобы просто добавить «\ n» добавить конец строки. (Это более общий подход, который я только что записал из памяти.)

Edit2: На самом деле, вы не можете его оптимизировать. Создание элемента данных arr в качестве «массива const char_type» с включенным «\ n» (вместо массива строковых литералов) использует какой-то причудливый вариационный код шаблона, который на самом деле немного длиннее (но он работает, см. Ответ Xeo).

Примечание: поскольку ct_string_vector (имя не является хорошим) хранит указатели, его следует использовать только со строками статической продолжительности хранения (такими как литералы или глобальные переменные). Преимущество состоит в том, что строка не должна копироваться и расширяться с помощью механизмов шаблонов. Если вы используете constexpr для хранения результата (например, в основном примере), компилятор должен жаловаться, если переданные параметры не относятся к статическому хранению.

 #include  #include  #include  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(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, 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 вам нужна правовая функция в первую очередь, а функции не могут вставлять и т. Д. Строковых литералов.

Если вы думаете об эквивалентном выражении в регулярной функции, это будет выделение памяти и объединение строк – определенно не поддающихся constexpr .

  • constexpr не компилируется в VC2013
  • Статический constexpr odr-используемый или нет?
  • Constexpr Математические функции
  • Таблица поиска с помощью constexpr
  • Обнаружение constexpr с помощью SFINAE
  • Можно ли использовать std :: string в constexpr?
  • Разница между `constexpr` и` const`
  • C ++ 11: вычисление времени вычисления массива
  • Поддерживается ли constexpr с lambda-функциями / выражениями?
  • Самоинициализация статической переменной constexpr, хорошо ли она сформирована?
  • constexpr и инициализация статического константного указателя void с реинтерпретом, который компилятор прав?
  • Давайте будем гением компьютера.