C ++ - фатальная ошибка LNK1120: 3 нерешенных внешних, не могут найти решение [duplicate]

Ваш код разбит на две полностью отдельные части, серверную сторону и клиентскую сторону .

                    |
               ---------->
              HTTP request
                    |
+--------------+    |    +--------------+
|              |    |    |              |
|    browser   |    |    |  web  server |
| (JavaScript) |    |    |  (PHP etc.)  |
|              |    |    |              |
+--------------+    |    +--------------+
                    |
  client side       |      server side
                    |
               <----------
          HTML, CSS, JavaScript
                    |

Обе стороны общаются через HTTP-запросы и ответы. PHP выполняется на сервере и выводит код HTML и, возможно, JavaScript, который отправляется как ответ клиенту, где интерпретируется HTML, и выполняется JavaScript. Когда PHP завершит вывод ответа, сценарий закончится, и на сервере ничего не произойдет, пока не появится новый HTTP-запрос.

Пример кода выполняется следующим образом:


Шаг 1, PHP выполняет весь код между тегами . В результате получилось следующее:


Вызов file_put_contents не привел ни к чему, он просто написал «+ foo +» в файл. Вызов привел к выводу «42», который теперь находится в том месте, где этот код использовался.

Этот итоговый код HTML / JavaScript теперь отправляется клиенту, где он получает оценку , Вызов alert работает, а переменная foo нигде не используется.

Весь PHP-код выполняется на сервере до того, как клиент даже начнет выполнение какого-либо JavaScript. В ответе JavaScript, с которым может взаимодействовать JavaScript, нет кода PHP.

Чтобы вызвать некоторый код PHP, клиент должен будет отправить новый HTTP-запрос на сервер. Это может произойти с использованием одного из трех возможных способов:

  1. Ссылка, которая заставляет браузер загружать новую страницу.
  2. Подача формы, которая передает данные на сервер и загружает новую страницу.
  3. Запрос AJAX , который является техникой Javascript, чтобы сделать обычный HTTP-запрос на сервер (например, 1. и 2. будет), но без оставляя текущую страницу.

Вот более подробный изложение этого метода

Вы также можете использовать JavaScript, чтобы браузер открыл новую страницу с помощью window.location или отправить форму, подражая возможностям 1 и 2.

380
задан Leon Timmermans 29 September 2008 в 16:56
поделиться

10 ответов

В последнем стандарте есть ключевое слово (export), которое поможет устранить эту проблему, но оно не реализовано ни в компилере, о котором я знаю, кроме Comeau.

Об этом можно узнать в FAQ-lite .

4
ответ дан Ben Collins 26 August 2018 в 02:37
поделиться
  • 1
    AFAIK, экспорт мертв, потому что они сталкиваются с новыми и новыми проблемами, каждый раз, когда они разрешают последнее, что делает общее решение все более сложным. И & quot; экспорт & quot; ключевое слово не позволит вам экспортировать & quot; от CPP в любом случае (по-прежнему от Х. Саттера). Поэтому я говорю: не задерживайте дыхание ... – paercebal 22 September 2008 в 17:25
  • 2
    Для реализации экспорта компилятор по-прежнему требует полного определения шаблона. Все, что вы получаете, это иметь его в виде скомпилированной формы. Но на самом деле нет никакого смысла. – Zan Lynx 12 March 2012 в 18:56
  • 3
    ... и это ушел из стандарта, из-за чрезмерного усложнения для минимального усиления. – DevSolar 26 August 2015 в 13:04

Нет ничего плохого в примере, который вы указали. Но я должен сказать, что считаю, что неэффективно хранить определения функций в файле cpp. Я понимаю только необходимость отделить декларацию и определение функции.

При использовании вместе с явным созданием класса, библиотека проверки возможностей Boost (BCCL) может помочь вам сгенерировать код функции шаблона в файлах cpp.

0
ответ дан Benoît 26 August 2018 в 02:37
поделиться

Это стандартный способ определения функций шаблона. Я думаю, что для определения шаблонов я читаю три метода. Или, возможно, 4. Каждый с плюсами и минусами.

  1. Определите определение класса. Мне это совсем не нравится, потому что я считаю, что определения классов строго для справки и должны быть легко прочитаны. Однако гораздо сложнее определить шаблоны в классе, чем внешние. И не все объявления шаблонов находятся на одном уровне сложности. Этот метод также делает шаблон истинным шаблоном.
  2. Определите шаблон в том же заголовке, но вне класса. Это мой любимый способ в большинстве случаев. Это упрощает определение вашего класса, шаблон остается истинным. Однако для этого требуется полное имя шаблона, которое может быть сложным. Кроме того, ваш код доступен всем. Но если вам нужно, чтобы ваш код был встроенным, это единственный способ. Вы также можете выполнить это, создав файл .INL в конце определений вашего класса.
  3. Включите header.h и implementation.CPP в ваш main.CPP. Я думаю, что так оно и было. Вам не нужно будет готовить какие-либо предварительные экземпляры, они будут вести себя как настоящий шаблон. Проблема, с которой я сталкиваюсь, заключается в том, что это не естественно. Обычно мы не включаем и не ожидаем включения исходных файлов. Думаю, с тех пор как вы включили исходный файл, функции шаблона могут быть встроены.
  4. Этот последний метод, который был опубликованным способом, определяет шаблоны в исходном файле, как и номер 3; но вместо того, чтобы включать исходный файл, мы предварительно создаем шаблоны для тех, которые нам понадобятся. У меня нет проблем с этим методом, и иногда это пригодится. У нас есть один большой код, он не может быть использован, поэтому просто поставьте его в файл CPP. И если мы знаем общие экземпляры, и мы можем предопределить их. Это спасает нас от написания в основном то же самое 5, 10 раз. Этот метод имеет преимущество в сохранении нашего кода. Но я не рекомендую вкладывать крошечные, регулярно используемые функции в файлы CPP. Поскольку это уменьшит производительность вашей библиотеки.

Заметьте, я не знаю о последствиях раздутого файла obj.

0
ответ дан Cássio Renan 26 August 2018 в 02:37
поделиться

Ваш пример правильный, но не очень портативный. Существует также несколько более чистый синтаксис, который можно использовать (как указано в @ namespace-sid).

Предположим, что шаблонный класс является частью некоторой библиотеки, которая должна быть разделена. Должны ли компилироваться другие версии шаблонного класса? Предполагается ли разработчик библиотеки предвидеть все возможные шаблонные применения класса?

Альтернативный подход - это небольшое изменение в отношении того, что у вас есть: добавьте третий файл, являющийся файлом реализации / создания шаблона.

foo.h file

// Standard header file guards omitted

template <typename T>
class foo
{
public:
    void bar(const T& t);
};

foo.cpp file

// Always include your headers
#include "foo.h"

template <typename T>
void foo::bar(const T& t)
{
    // Do something with t
}

foo-impl.cpp file

// Yes, we include the .cpp file
#include "foo.cpp"
template class foo<int>;

caveat заключается в том, что вам нужно сообщить компилятору компилировать foo-impl.cpp вместо foo.cpp, поскольку компиляция последнего ничего не делает.

Конечно, вы можете иметь несколько реализаций в третьем файле или иметь несколько файлов реализации для каждого типа, который вы хотели бы использовать.

Это дает гораздо большую гибкость при совместном использовании шаблона для других целей.

Эта настройка также сокращает время компиляции для повторно используемых классов, повторно не перекомпилировать один и тот же заголовочный файл в каждой единицы перевода.

4
ответ дан Cameron Tacklind 26 August 2018 в 02:37
поделиться
  • 1
    что вы покупаете? Вам необходимо изменить foo-impl.cpp, чтобы добавить новую специализацию. – MK. 22 March 2017 в 15:16
  • 2
    Разделение деталей реализации (также определений в foo.cpp), из которых версии фактически скомпилированы (в foo-impl.cpp) и деклараций (в foo.h). Мне не нравится, что большинство шаблонов C ++ полностью определены в файлах заголовков. Это противоречит стандарту C / C ++ пар c[pp]/h для каждого класса / пространства имен / любой группы, которую вы используете. Люди, похоже, все еще используют монолитные файлы заголовков просто потому, что эта альтернатива широко не используется или не известна. – Cameron Tacklind 29 March 2017 в 20:06

Время обновления! Создайте встроенный (.inl или, возможно, любой другой) файл и просто скопируйте все свои определения в нем. Не забудьте добавить шаблон над каждой функцией (template <typename T, ...>). Теперь вместо включения файла заголовка в встроенный файл вы делаете обратное. Включите встроенный файл после объявления вашего класса (#include "file.inl").

Я действительно не знаю, почему никто не упомянул об этом. Я не вижу немедленных недостатков.

-1
ответ дан Didii 26 August 2018 в 02:37
поделиться
  • 1
    Непосредственные недостатки - это принципиально то же самое, что просто определить функции шаблона непосредственно в заголовке. Когда вы #include "file.inl", препроцессор собирается вставить содержимое file.inl прямо в заголовок. Независимо от причины, по которой вы хотели избежать реализации в заголовке, это решение не решает эту проблему. – Cody Gray♦ 25 June 2013 в 04:38
  • 2
    - и означает, что вы технически необоснованно обременяете себя заданием написать весь подробный, умопомрачительный шаблон, необходимый для определения out-of-line template. Я понимаю, почему люди хотят это сделать - для достижения максимальной четкости с не-шаблонами объявлений / определений, чтобы поддерживать декларацию интерфейса, выглядящую аккуратно, и т. Д. - но это не всегда стоит хлопот. Это случай оценки компромиссов с обеих сторон и выбор наименее плохой . ... до namespace class становится вещью: O [, пожалуйста, будь то ] – underscore_d 2 August 2016 в 20:52
  • 3
    @underscore_d, который определенно должен стать чем-то ... – Andrew 14 September 2016 в 09:43
  • 4
    @Andrew Кажется, что он застрял в трубах Комитета, хотя я думаю, что видел, что кто-то сказал, что это не было преднамеренным. Хотелось бы, чтобы это превратилось в C ++ 17. Может быть, следующее десятилетие. – underscore_d 14 September 2016 в 10:05
  • 5
    @CodyGray: Технически, это действительно то же самое для компилятора, и поэтому не сокращает время компиляции. Тем не менее, я думаю, что это стоит упомянуть и практиковать в ряде проектов, которые я видел. Переход по этой дороге помогает отделить интерфейс от определения, что является хорошей практикой. В этом случае это не помогает с совместимостью ABI и т.п., но облегчает чтение и понимание интерфейса. – kiloalphaindia 7 May 2018 в 09:29

Этот код хорошо сформирован. Вам нужно только обратить внимание на то, что определение шаблона видимо в момент создания экземпляра. Чтобы процитировать стандарт, § 14.7.2.4:

Определение шаблона неэкспортируемой функции, неэкспортируемый шаблон функции-члена или неэкспортированная функция-член или статический член данных шаблон шаблона должен присутствовать в каждой единицы перевода, в которой он явно создан.

16
ответ дан Konrad Rudolph 26 August 2018 в 02:37
поделиться
  • 1
    Что означает не экспортируемый ? – Dan Nissenbaum 12 June 2014 в 20:29
  • 2
    @Dan Видимый только внутри его блока компиляции, а не вне его. Если вы связываете несколько единиц компиляции вместе, экспортируемые символы могут использоваться через них (и должны иметь один или, по крайней мере, в случае шаблонов, согласованные определения, иначе вы запускаете UB). – Konrad Rudolph 12 June 2014 в 20:32
  • 3
    Благодарю. Я думал, что все функции (по умолчанию) видны вне блока компиляции. Если у меня есть две единицы компиляции a.cpp (определяющие функцию a() {}) и b.cpp (определяющие функцию b() { a() }), то это будет успешно связываться. Если я прав, то приведенная цитата, похоже, не будет применяться к типичному делу ... я что-то не так? – Dan Nissenbaum 12 June 2014 в 20:46
  • 4
    @Dan Тривиальный контрпример: функции inline – Konrad Rudolph 12 June 2014 в 21:02
  • 5
    Шаблоны @Dan Function неявно inline. Причина в том, что без стандартизованного C ++ ABI трудно / невозможно определить эффект, который в противном случае он имел бы. – Konrad Rudolph 12 June 2014 в 22:41

Да, это стандартный способ выполнения специализированного экземпляра specializiation. Как вы сказали, вы не можете создать этот шаблон с другими типами.

Изменить: исправлено на основе комментария.

3
ответ дан Lou Franco 26 August 2018 в 02:37
поделиться
  • 1
    Будучи разборчивым по терминологии, это «явный экземпляр». – Richard Corden 22 September 2008 в 17:19

Это должно хорошо работать везде, где поддерживаются шаблоны. Явное создание экземпляра шаблона является частью стандарта C ++.

10
ответ дан moonshadow 26 August 2018 в 02:37
поделиться

Для других на этой странице, интересующихся, что такое правильный синтаксис (как и я) для явной специализированности шаблонов (или, по крайней мере, в VS2008), его следующее ...

В вашем .h-файле. ..

template<typename T>
class foo
{
public:
    void bar(const T &t);
};

И в вашем .cpp файле

template <class T>
void foo<T>::bar(const T &t)
{ }

// Explicit template instantiation
template class foo<int>;
87
ответ дан Qix 26 August 2018 в 02:37
поделиться
  • 1
    Чтобы дополнить ответ, ссылка ссылается на вопрос положительно, т. Е. Можно сделать то, что предложил Роб, и иметь код для переносимости. – ivotron 1 May 2011 в 22:46
  • 2
    Вы имеете в виду & quot; для явного шаблона шаблона CLASS ?. В этом случае это будет охватывать каждую функцию, которую имеет шаблонный класс? – Arthur 21 February 2013 в 15:57
  • 3
    Можете ли вы просто опубликовать соответствующие части в самом ответе? Почему такая ссылка даже разрешена на SO. Я не знаю, что искать в этой ссылке, поскольку с тех пор она сильно изменилась. – Ident 16 August 2015 в 21:57

Это определенно не неприятный хак, но имейте в виду, что вам нужно будет сделать это (явная спецификация шаблона) для каждого класса / типа, который вы хотите использовать с данным шаблоном. В случае МНОГИХ типов, запрашивающих создание шаблона, может быть много строк в вашем .cpp-файле. Чтобы исправить эту проблему, вы можете иметь TemplateClassInst.cpp в каждом проекте, который вы используете, чтобы у вас было больше контроля над тем, какие типы будут созданы. Очевидно, что это решение не будет идеальным (ака серебряная пуля), так как вы можете сломать ODR:).

5
ответ дан Red XIII 26 August 2018 в 02:37
поделиться
  • 1
    Вы уверены, что это нарушит ODR? Если строки экземпляра в TemplateClassInst.cpp относятся к идентичному исходному файлу (содержащему определения функции шаблона), не гарантируется ли это, что он не нарушает ODR, поскольку все определения идентичны (даже если они повторяются)? – Dan Nissenbaum 12 June 2014 в 20:36
  • 2
    Пожалуйста, что такое ODR? – nonremovable 22 June 2018 в 11:45
Другие вопросы по тегам:

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