Ошибки компоновщика с массивами constexpr [duplicate]

Попробуйте

<?php
$to = "somebody@example.com, somebodyelse@example.com";
$subject = "HTML email";

$message = "
    <html>
    <head>
       <title>HTML email</title>
    </head>
    <body>
      <p>This email contains HTML Tags!</p>
      <table>
        <tr>
         <th>Firstname</th>
         <th>Lastname</th>
        </tr>
        <tr>
          <td>John</td>
          <td>Doe</td>
        </tr>
      </table>
    </body>
    </html>";

// Always set content-type when sending HTML email
$headers = "MIME-Version: 1.0" . "\r\n";
$headers .= "Content-type:text/html;charset=UTF-8" . "\r\n";

// More headers
$headers .= 'From: <webmaster@example.com>' . "\r\n";
$headers .= 'Cc: myboss@example.com' . "\r\n";

mail($to,$subject,$message,$headers);
?> 
147
задан Pubby 18 April 2016 в 18:02
поделиться

5 ответов

Добавить в файл cpp:

constexpr char foo::baz[];

Причина: вы должны указать определение статического члена, а также объявление. Объявление и инициализатор входят в определение класса, но определение члена должно быть отдельным.

129
ответ дан Kerrek SB 21 August 2018 в 13:12
поделиться
  • 1
    Это выглядит странно ... поскольку он, похоже, не предоставляет компилятору некоторую информацию, которой он не был раньше ... – vines 20 September 2012 в 00:13
  • 2
    Это выглядит еще более странно, когда у вас есть объявление класса в файле .cpp! Вы инициализируете поле в объявлении класса, но вам все равно нужно & quot; declare & quot; поле, написав над классом constexpr char foo :: baz []. Похоже, что программисты, использующие constexpr, могут скомпилировать свои программы, следуя одному странному кончику: объявите его снова. – Lukasz Czerwinski 19 March 2014 в 20:36
  • 3
    @LukaszCzerwinski: Слово, которое вы ищете, это «define». – Kerrek SB 19 March 2014 в 21:13
  • 4
    @NeilKirk: Это обсуждение очень запутано. constexpr - это еще одна, не имеющая отношения вещь, не связанная непосредственно с привязкой. – Kerrek SB 15 August 2014 в 13:21
  • 5
    как будет выглядеть выражение, если foo будет templatized? Благодарю. – Hei 13 July 2015 в 14:22

Не является более элегантным решением изменить char[] на:

static constexpr char * baz = "quz";

Таким образом, мы можем иметь определение / объявление / инициализатор в 1 строке кода.

2
ответ дан Neel Basu 21 August 2018 в 13:12
поделиться
  • 1
    с помощью char[] вы можете использовать sizeof, чтобы получить длину строки во время компиляции, при этом char * вы не можете (в этом случае он будет возвращать ширину типа указателя, 1). – gnzlbg 10 May 2016 в 10:52
  • 2
    Это также генерирует предупреждение, если вы хотите быть строгим с ISO C ++ 11. – ShitalShah 12 July 2017 в 04:11

Это действительно недостаток в C ++ 11, как объяснили другие, в C ++ 11 статическая переменная-член constexpr, в отличие от любой другой глобальной переменной constexpr, имеет внешнюю связь, поэтому она должна быть явно определена где-то.

Также стоит отметить, что вы часто можете избежать со статическими переменными-членами constexpr без определений при компиляции с оптимизацией, так как они могут оказаться вложенными во все виды использования, но если вы легко компилируете без оптимизации, ваша программа не будет связываться. Это делает эту очень распространенную скрытую ловушку - ваша программа отлично компилируется с оптимизацией, но как только вы отключите оптимизацию (возможно, для отладки), она не может связываться.

Хорошая новость: этот недостаток исправлен в C ++ 17! Этот подход немного запутан, хотя: в C ++ 17 статические константные переменные-члены-константы неявно встроены . Наличие inline, применяемого к переменным , является новой концепцией в C ++ 17, но это фактически означает, что в любом месте они не нуждаются в явном определении.

23
ответ дан SethML 21 August 2018 в 13:12
поделиться
  • 1
    Информация для C ++ 17. Вы можете добавить эту информацию в принятый ответ! – S.R 9 November 2017 в 10:15

C ++ 17 вводит встроенные переменные

C ++ 17 исправляет эту проблему для константных переменных-членов constexpr, требующих определения вне строки, если она была использована ord. См. Первоначальный ответ ниже для деталей pre C ++ 17.

Предложение P0386 Inline Variables вводит возможность применения спецификатора inline к переменным. В частности, для этого случая constexpr подразумевает inline для статических переменных-членов. В предложении говорится:

Встроенный спецификатор может применяться как к переменным, так и к функциям. Переменная, объявленная inline, имеет ту же семантику, что и функция, объявленная inline: она может быть определена идентично в нескольких единицах перевода, должна определяться в каждой единицы перевода, в которой она одурена, а поведение программы выглядит так, как будто ровно одна переменная.

и изменено [basic.def] p2:

Объявление является определением, если ...

  • it объявляет статический элемент данных вне определения класса, и переменная была определена внутри класса с помощью спецификатора constexpr (это использование устарело, см. [des.static_constexpr]),

...

и добавьте [des.static_constexpr] :

Для совместимости с предыдущими стандартами C ++, статический элемент данных constexpr может быть избыточно обновляется вне класса без инициализатора. Это использование устарело. [Пример:

struct A {
  static constexpr int n = 5;  // definition (declaration in C++ 2014)
};

constexpr int A::n;  // redundant declaration (definition in C++ 2014)

- конец примера]

Оригинальный ответ

В C ++ 03 нам разрешалось предоставлять только в классе intializers для const-интегралов или const перечисления типов , в C ++ 11 с использованием constexpr это было расширено до типов литералов .

В C ++ 11 нам не нужно предоставлять определение области пространства имен для статического члена constexpr, если это не odr-used , мы можем видеть это из черновика Стандартная секция C ++ 11 9.4.2 [class.static.data] , в которой говорится ( акцент мой вперед ):

[ ...] Статический член данных типа literal может быть объявлен в определении класса с помощью спецификатора constexpr; если это так, в его декларации указывается логический или равный-инициализатор, в котором каждое предложение-инициализатор, являющееся выражением-присваиванием, является постоянным выражением. [Примечание. В обоих случаях член может отображаться в постоянных выражениях. -End note] Элемент все еще должен быть определен в области пространства имен, если он используется в odr (3.2) в программе, а определение области пространства имен не должно содержать инициализатор.

Итак, тогда вопрос будет, есть baz odr-used здесь:

std::string str(baz); 

, а ответ да , и поэтому нам нужна область пространства имен определение.

Итак, как мы определяем, является ли переменная odr-used ? В исходной формулировке C ++ 11 в разделе 3.2 [basic.def.odr] говорится:

Выражение потенциально оценивается, если оно не является неоцененным операндом ( Пункт 5) или его подвыражение. Переменная, имя которой отображается как потенциально оцениваемое выражение, используется odr, если оно не является объектом, удовлетворяющим требованиям для появления в постоянном выражении (5.19), и немедленно применяется преобразование lvalue-to-rval (4.1).

Таким образом, baz дает константное выражение , но преобразование lvalue-to-rvalue не применяется сразу, поскольку оно неприменимо из-за к baz, являющемуся массивом. Это описано в разделе 4.1 [conv.lval] , в котором говорится:

Значение gl (3.10) нефункционного типа без массива T может преобразуется в prvalue.53 [...]

Что применяется в преобразовании преобразования в массив .

Это формулировка [basic.def.odr] была изменена из-за отчета о дефектах 712 , поскольку некоторые случаи не были охвачены этой формулировкой, но эти изменения не изменяют результаты для этого случая .

47
ответ дан Shafik Yaghmour 21 August 2018 в 13:12
поделиться
  • 1
    так ясно, что constexpr не имеет к этому никакого отношения? (baz - постоянное выражение) – M.M 4 March 2015 в 05:23
  • 2
    @MattMcNabb хорошо constexpr требуется, если элемент не является integral or enumeration type, но в остальном, да, важно, что это выражение константа . – Shafik Yaghmour 4 March 2015 в 05:35
0
ответ дан Josh Greifer 1 November 2018 в 07:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: