Error c2908 явная специализация уже создан экземпляр

Ошибки компилятора с C2900 по C2999 В статьях в этом разделе документации объясняется подмножество сообщений об ошибках, создаваемых компилятором. Компиляторы и средства сборки Visual Studio могут сообщать о различных типах ошибок и предупреждений. После того как обнаружена ошибка или предупреждение, средства сборки могут делать предположения о намерении кода и пытаться продолжить, чтобы в то […]

Содержание

  1. Ошибки компилятора с C2900 по C2999
  2. Явная специализация после создания экземпляра
  3. 2 ответы
  4. Ошибка при создании явной специализации. В чём ошибка?
  5. Явная реализация специализации шаблона функции
  6. 2 ответа
  7. Явная реализация специализации шаблона функции
  8. Решение
  9. Другие решения

Ошибки компилятора с C2900 по C2999

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

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

Чтобы получить справку о конкретном диагностическом сообщении в Visual Studio, выберите его в окне вывода и нажмите клавишу F1 . Visual Studio открывает страницу документации для этой ошибки, если она существует. Вы также можете использовать средство поиска в верхней части страницы, чтобы найти статьи о конкретных ошибках или предупреждениях. Или просмотрите список ошибок и предупреждений по инструменту и введите оглавление на этой странице.

Не все ошибки или предупреждения Visual Studio описаны. Во многих случаях диагностическое сообщение предоставляет все доступные сведения. Если вы приземлились на этой странице при использовании F1 , и вы думаете, что сообщение об ошибке или предупреждении нуждается в дополнительном объяснении, сообщите нам об этом. Кнопки обратной связи на этой странице можно использовать для устранения проблемы с документацией на сайте GitHub. Если вы считаете ошибку или предупреждение неправильным или обнаружили еще одну проблему с набором инструментов, сообщите о проблеме продукта на сайте Сообщество разработчиков. Вы также можете отправлять отзывы и вводить ошибки в интегрированной среде разработки. В Visual Studio перейдите в строку меню и выберите «Отправить > отзыв о > проблеме» или отправьте предложение с помощью отправки > отзывов > справки.

Вы можете найти дополнительную помощь по ошибкам и предупреждениям на форумах Microsoft Learn Q&A . Или найдите номер ошибки или предупреждения на сайте Сообщество разработчиков Visual Studio C++. Вы также можете выполнить поиск решений в Stack Overflow .

Ссылки на дополнительные справочные материалы и ресурсы сообщества см. в справке и сообществе Visual C++.

Источник

Явная специализация после создания экземпляра

У меня есть следующий код:

Я получаю следующее сообщение об ошибке:

на отмеченной линии.
Компилятор настаивает на том, что он уже создал экземпляр DoSomething > , а это невозможно, и простая программа может это доказать:

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

2 ответы

Код запросил неявное создание экземпляра на DoSomething(*i) . Тот факт, что вы не определили шаблон в этой единице перевода, означает, что он не может создать экземпляр специализации, поэтому DoSomething(*i) выдает ошибку «неразрешенного символа» (компоновщика) в вашем случае. Чтобы избавиться от этой ошибки, вы должны либо определить шаблон в этом TU, либо предоставить явную директиву создания экземпляра этого шаблона в TU, где вы определяете шаблон.

Сам факт, что код запросил неявное создание экземпляра для специализации. DoSomething > прежде, чем вы явно указали, что специализации достаточно для того, чтобы программа стала плохо сформированной (хотя и без диагностики; компилятор здесь хорошо справляется, чего не требуется).

Как любезно отмечает @CharlesBailey, объявления явной специализации вполне достаточно; определение этого может быть дано в другом месте, даже за пределами использования TU.

ответ дан 14 окт ’11, 23:10

«без необходимости диагностики»: требуется ли вообще диагностика в стандарте? — Дани

Я думаю часть «Тот факт, что код запрашивал неявное создание экземпляра для специализации DoSomething > прежде, чем вы явно указали, что специализации достаточно, чтобы программа стала плохо сформированной « неверно, или в нем чего-то не хватает. Скорее должно быть: Сам факт, что код запросил неявное создание экземпляра для специализации. DoSomething > от полная специализация до того, как вы явно указали, что специализации достаточно, чтобы программа стала плохо сформированной. Поправьте меня если я ошибаюсь. 🙂 — Наваз

@Nawaz: значит, вы оба говорите, что неявное отличается от явного до уровня, который нельзя поменять местами? — Дани

Вам нужно только объявлять явная специализация перед кодом, который вызовет неявное создание экземпляра специализации. Определение может быть дано в другом месте. — CB Бейли

@Nawaz То, что я написал, вызывает неявное создание экземпляра. Вы получаете плохо сформированную (NDR) программу, даже если вызываете неявное создание экземпляра из шаблона функции: template void f() < DoSomething >(vector ()); > перед предоставлением явной специализации. — Йоханнес Шауб — litb

Обычно это просто означает, что вы не предоставили «прототип» для специализации шаблона. Другими словами, вы не предупредили компилятор, что «эй, для этого конкретного типа функции будет специализация, так что не вставляйте неправильную».

В моем случае у меня была специализация шаблона в .cpp файл и получил эту ошибку. Предоставление «прототипа функции» (который представляет собой просто заголовок специализации шаблона, за которым следует точка с запятой, как и обычный прототип функции) устранил проблему.

Источник

Ошибка при создании явной специализации. В чём ошибка?

Делаю 6 упражнение 8 главы книги «Язык программирования C++» Стивен Пратта

Напишите шаблонную функцию maxn(), которая принимает в качестве
аргумента массив элементов типа т и целое число, представляющее количество
элементов в массиве, а возвращает элемент с наибольшим значением. Протестируйте
ее работу в программе, которая использует этот шаблон с массивом из шести
значений int и массивом из четырех значений double.
Программа также должна включать специализацию, которая использует массив указателей на char в
качестве первого аргумента и количество указателей — в качестве второго, а затем
возвращает адрес самой длинной строки. Если имеется более одной строки
наибольшей длины, функция должна вернуть адрес первой из них. Протестируйте
специализацию на массиве из пяти указателей на строки.

у меня выходит ошибка в VS19 :no instance of function template «maxn» matches the specified type.
Что я не так делаю?

  • Вопрос задан более двух лет назад
  • 173 просмотра

Это — общая форма шаблона функции. Если тебе нужно сделать явную специализацию шаблона, твоя явная специализация должна соответствовать этому общему шаблону.

Сырые однобайтовые строки в C++ (при соблюдении правила нуль-терминации) определяются типом char* или const char* если строку не планируется модифицировать. Наша функция не планирует модифицировать строки, следовательно явная специализация шаблона должна работать с типом const char* .
Тип const char* должен быть подставлен везде вместо параметра шаблона T .

Таким образом явная специализация шаблона для сырых строк будет полностью соответствовать общему шаблону функции.

А теперь немного о твоей ошибке.

Это объявление не является явной специализацией приведенного выше шаблона. Это — определение перегрузки функции через явную специализацию какого-то иного неописанного шаблона. Это потому что результат функции char* отличен от параметра функции const char* .
В целом, эта запись является плохо сформулированной (ill-formed) по целому ряду причин, включая и то, что перегрузка не может осуществляться через изменение типа результата функции.

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

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

Источник

Явная реализация специализации шаблона функции

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

A.h (основной шаблон, специализация шаблона, внешний)

A.cpp (явное создание)

B.cpp (включает A.h)

main.cpp

Компилятор выдает сбой: «несколько определений ‘void foo ()’. Я думал, что extern должен позаботиться об этом. Модуль компиляции B не должен создавать экземпляр foo, а вместо этого использовать экземпляр A во время компоновки, нет ? Что я здесь не так?

Обратите внимание: если я не специализируюсь на foo, код компилируется нормально. Есть ли какой-то конфликт между специализацией функций и созданием экземпляров?

2 ответа

Для подавления создания экземпляра здесь не требуется extern . Объявляя явную специализацию, вы уже говорите любому коду, который вызывает foo , использовать явную специализацию, а не основной шаблон. Вместо этого вы просто хотите объявить специализацию в A.h, а затем определить ее в A.cpp:

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

Поскольку ответ Брайана дает вам рабочее решение и объясняет, почему вам не нужен extern, я немного подробнее расскажу о вашей ситуации.

Во-первых, вы заявили, что ваш компилятор дает сбой, поэтому вы предполагали, что это ошибка компилятора. На самом деле это не так. С вашим исходным кодом A.cpp , B.cpp и main.cpp успешно скомпилированы сами по себе. Ошибок компиляции не было. В Visual Studio 2017 CE только после того, как я попытался собрать программу, произошел сбой. Вот моя ошибка сборки IDE.

У вас есть ошибка Linking . Не удается установить ссылку из-за нескольких определенных символов.

Давайте рассмотрим ваш код:

А.ч

main.cpp

Здесь все 3 компилируются, но когда он переходит к компоновщику для создания одного исполняемого файла, передавая три объектных файла, он терпит неудачу. Компилятор просто проверяет грамматику языка — синтаксис и преобразует его в объектные файлы. Компоновщик получает объектные файлы от компилятора и создает все необходимые символы для переменных, функций, классов и т. Д.

Он смотрит в main и видит #include «A.h» и #include «B.h» Итак, предварительный компилятор уже выполнил замену текста и вставил A.h и B.h вверху страницы, так что все код, который был в A.h и B.h , который принадлежит единицам перевода A.cpp и B.cpp , теперь также находится в единице перевода main.cpp . Таким образом, он видит объекты шаблона foo и видит несколько определений! Это не имеет ничего общего с использованием extern. Чтобы продемонстрировать это, я могу удалить часть вашего кода и по-прежнему генерировать ту же ошибку сборки, при которой не удается установить ссылку из-за нескольких определений.

А.ч

main.cpp

Дайте в основном такую же ошибку связывания сборки:

Единственное различие между ними состоит в том, что множественное определение находится в A и B , где во втором экземпляре даже без использования B найдено множественное определение A .

Чтобы решить эту проблему, используйте Brian’s ответ! По сути, эмпирическое правило — иметь declarations в headers и definitions в cpp файлах, если только ваши определения не находятся в конкретном классе или пространстве имен, а не в глобальной области видимости.

Источник

Явная реализация специализации шаблона функции

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

хиджры (основной шаблон, шаблонная специализация, extern)

a.cpp (явная реализация)

B.h

B.cpp (включает A.h)

main.cpp

На меня вылетает компилятор: «несколько определений void foo ()». Я думал, что extern должен был позаботиться об этом. Модуль компиляции B не должен создавать экземпляр foo, а вместо этого использовать экземпляр A во время ссылки, нет ? Что я тут не так делаю?

Обратите внимание, что если я не специализируюсь на foo, код компилируется просто отлично. Есть ли какой-то конфликт между специализацией функций и реализацией?

Решение

Вам не нужно extern здесь, чтобы подавить создание экземпляров. Объявляя явную специализацию, вы уже сообщаете любой код, который вызывает foo использовать явную специализацию, а не основной шаблон. Вместо этого вы просто хотите объявить специализацию в A.h, а затем определить ее в A.cpp:

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

Другие решения

Поскольку ответ Брайана дает вам рабочее решение и объясняет, почему вам не нужен extern, я подробнее остановлюсь на вашей ситуации.

Во-первых, вы заявили, что ваш компилятор падает на вас, поэтому вы предполагали, что это ошибка компилятора. Это не фактический случай. С вашим кодом, как это было, A.cpp , B.cpp & main.cpp все успешно скомпилировано самостоятельно. Не было ошибок компиляции. В Visual Studio 2017 CE не было, пока я не попытался собрать программу, пока она не рухнула. Вот ошибка сборки моей IDE.

Что у вас здесь есть Linking ошибка. Не удается связать из-за нескольких определенных символов.

Давайте рассмотрим ваш код:

хиджры

a.cpp

B.h

B.cpp

main.cpp

Здесь происходит то, что все 3 компилируются, но когда он переходит к компоновщику для создания одного исполняемого файла, передавая три объектных файла, он терпит неудачу. Компилятор просто проверяет синтаксис грамматики языка и преобразует его в объектные файлы. Компоновщик получает объектные файлы от компилятора и создает все необходимые символы для переменных, функций, классов и т. Д.

Выглядит в основном и видит #include «A.h» а также #include «B.h» Таким образом, предварительный компилятор уже сделал подстановку текста и вставил A.h а также B.h в верхней части страницы, так что весь код, который был в A.h & B.h что принадлежит A.cpp а также B.cpp переводчики также в настоящее время в main.cpp переводческий блок. Так что он видит foo объекты шаблона, и это, случается, видит больше чем одно определение! Это не имеет ничего общего с вашим использованием extern. Чтобы продемонстрировать это, я могу удалить часть вашего кода и по-прежнему генерировать ту же ошибку сборки, где он не может связаться из-за нескольких определений.

хиджры

a.cpp

main.cpp

Дайте в основном ту же ошибку компоновки сборки:

Единственное различие между ними состоит в том, что множественное определение находится в A & B где во 2-м экземпляре даже не используя B найдено несколько определений A ,

Чтобы разрешить это использование Brian’s ответ! Основное правило заключается в том, чтобы иметь declarations в headers а также definitions в cpp файлы, если только ваши определения не находятся в определенном классе или пространстве имен, а не в глобальной области видимости.

Источник

I’m using templates for implementing the CRTP pattern. With the code below I’m getting linker errors (for all the methods that are defined in the base class CPConnectionBase) like this:

error LNK2001: unresolved external symbol «public: void __thiscall CPConnectionBase::start(void)» (?start@?$CPConnectionBase@VTNCPConnection@@@@QAEXXZ)

I guess the solution here is explicit template instantiation. And indeed I can build my code when I add

#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;

to the file CPConnectionBase.cpp. This is certainly the wrong place since I don’t want to include the header of all possible derived classes into the source of the base class (I might want to use the base class also in another project with other derived classes).

So my goal is to instantiate the template in the source file of the derived class (TNCPConnection.h or TNCPConnection.cpp), but I couldn’t find a solution. Adding

template class CPConnectionBase<TNCPConnection>;

to the file TNCPConnection.cpp does not solve my linker problems, and adding

template<> class CPConnectionBase<TNCPConnection>;

to the file TNCPConnection.cpp gives me a compile time error:

error C2908: explicit specialization; ‘CPConnectionBase’ has already been instantiated

How can I get rid of the linker errors without making the implementation of the base class dependent of the header files of the derived classes?

Here is the skeleton of my code:

CPConnectionBase.h

template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
    void start();
};

CPConnectionBase.cpp

#include "stdafx.h"
#include "CPConnectionBase.h"

template<class Derived>
void CPConnectionBase<Derived>::start()
{
    ...
}

TNCPConnection.h

#include "CPConnectionBase.h"

class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
    void foo(void);
};

TNCPConnection.cpp

#include "stdafx.h"
#include "TNCPConnection.h"

void TNCPConnection::foo(void)
{
    ...
}

I’m using templates for implementing the CRTP pattern. With the code below I’m getting linker errors (for all the methods that are defined in the base class CPConnectionBase) like this:

error LNK2001: unresolved external symbol «public: void __thiscall CPConnectionBase::start(void)» (?start@?$CPConnectionBase@VTNCPConnection@@@@QAEXXZ)

I guess the solution here is explicit template instantiation. And indeed I can build my code when I add

#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;

to the file CPConnectionBase.cpp. This is certainly the wrong place since I don’t want to include the header of all possible derived classes into the source of the base class (I might want to use the base class also in another project with other derived classes).

So my goal is to instantiate the template in the source file of the derived class (TNCPConnection.h or TNCPConnection.cpp), but I couldn’t find a solution. Adding

template class CPConnectionBase<TNCPConnection>;

to the file TNCPConnection.cpp does not solve my linker problems, and adding

template<> class CPConnectionBase<TNCPConnection>;

to the file TNCPConnection.cpp gives me a compile time error:

error C2908: explicit specialization; ‘CPConnectionBase’ has already been instantiated

How can I get rid of the linker errors without making the implementation of the base class dependent of the header files of the derived classes?

Here is the skeleton of my code:

CPConnectionBase.h

template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
    void start();
};

CPConnectionBase.cpp

#include "stdafx.h"
#include "CPConnectionBase.h"

template<class Derived>
void CPConnectionBase<Derived>::start()
{
    ...
}

TNCPConnection.h

#include "CPConnectionBase.h"

class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
    void foo(void);
};

TNCPConnection.cpp

#include "stdafx.h"
#include "TNCPConnection.h"

void TNCPConnection::foo(void)
{
    ...
}

Я использую шаблоны для реализации шаблона CRTP. В приведенном ниже коде я получаю ошибки компоновщика (для всех методов, определенных в базовом классе CPConnectionBase) как это:

ошибка LNK2001: неразрешенный внешний символ «public: void __thiscall CPConnectionBase :: start (void)» (? start @? $ CPConnectionBase @ VTNCPConnection @@@@ QAEXXZ)

Думаю, решение здесь — явное создание шаблона. И действительно, я могу построить свой код, если добавлю

#include "TNCPConnection.h"
template class CPConnectionBase<TNCPConnection>;

в файл CPConnectionBase.cpp. Это, безусловно, неправильное место, поскольку я не хочу включать заголовок всех возможных производных классов в источник базового класса (я мог бы использовать базовый класс также в другом проекте с другими производными классами).

Итак, моя цель — создать экземпляр шаблона в исходном файле производного класса (TNCPConnection.h или TNCPConnection.cpp), но я не смог найти решения. Добавление

template class CPConnectionBase<TNCPConnection>;

в файл TNCPConnection.cpp не решает мои проблемы с компоновщиком, и добавление

template<> class CPConnectionBase<TNCPConnection>;

в файл TNCPConnection.cpp дает мне ошибку времени компиляции:

ошибка C2908: явная специализация; CPConnectionBase уже создан.

Как я могу избавиться от ошибок компоновщика, не делая реализацию базового класса зависимой от файлов заголовков производных классов?

Вот скелет моего кода:

CPConnectionBase.h

template <class Derived>
class CPConnectionBase : public boost::enable_shared_from_this<Derived>
{
public:
    void start();
};

CPConnectionBase.cpp

#include "stdafx.h"
#include "CPConnectionBase.h"

template<class Derived>
void CPConnectionBase<Derived>::start()
{
    ...
}

TNCPConnection.h

#include "CPConnectionBase.h"

class TNCPConnection : public CPConnectionBase<TNCPConnection>
{
public:
    void foo(void);
};

TNCPConnection.cpp

#include "stdafx.h"
#include "TNCPConnection.h"

void TNCPConnection::foo(void)
{
    ...
}

Всем добрый вечер!

Я задумался над тем почему и как работает CRTP в C++.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
 
#include <iostream>
 
using std::cout;
using std::endl;
 
template < class T >
class Base
{
public:
    void interface()
    {
        cout << __PRETTY_FUNCTION__ << endl;
        static_cast< T* >( this )->impl();
    }
 
    static void foo()
    {
        cout << __PRETTY_FUNCTION__ << endl;
        T::bar();
    }
 
private:
    // T t; // will not compile due to the fact that T isn't complete type
};
 
class Derived : public Base< Derived >
{
public:
    void impl()
    {
        cout << __PRETTY_FUNCTION__ << endl;
    }
 
    static void bar()
    {
        cout << __PRETTY_FUNCTION__ << endl;
    }
};
 
int main()
{
    Derived d;
    d.interface();
 
    Base< Derived >::foo();
 
    return 0;
}
 
// pseudo-code of template instantiation
template<>
class Base< Derived >
{
public:
    void interface()
    {
        // ...
        static_cast< Derived* >( this )->impl();
    }
    
    static void foo()
    {
        // ...
        Derived::bar();
    }
};

Поправьте пожалуйста если я не прав/не точен:

Я понимаю что такое неполный тип здесь …

C++
1
2
3
4
struct Node
{
    Node* next_;
};

… так как next_ это просто адресс какого-то объекта типа T. Во время компиляции система типов проверяет только объявление типа T, адресс которого хранится в указателе, для того чтобы предотвратить операции которые невозможны для этого типа.

CRTP:
1) Компилятор находит выражение Derived d; — в этом время тип Derived ещё не является полным, потому что базовый класс Base< Derived > не инстанциирован.
2) Начинается инстанциирование шаблона Base< Derived >, во время которого шаблонный тип Derived остается неполным.
3) После того как Base< Derived > истанциирован, Base< Derived > становится «полным» типом, Derived при это всё ещё является неполным.
4) После чего происходит создание класса

Derived : public Base< Derived >

, здесь уже ничего интересного не происходит. Теперь Derived является полным типом.

Получается что Derived находящийся слева от двоеточия и шаблонный тип Derived внутри Base — это два разных типа?

Извините за неточности/грубый подход, у меня мозг сломался пока я пытался понять что происходит. Всё время казалось что некоторого рода рекурсивное отношение происходит между Derived & Base< Derived > в случае наследования. Помогите поставить мозги на место) Спасибо.

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь

Понравилась статья? Поделить с друзьями:
  • Error c2784 c
  • Error c2760 синтаксическая ошибка непредвиденный токен идентификатор требуется спецификатор типа
  • Error c2731 winmain функция не может быть перегружена
  • Error c2712 cannot use try in functions that require object unwinding
  • Error c2679 бинарный не найден оператор принимающий правый операнд типа