Error specialization of after instantiation

Ошибка: specialization of '.' after instantiation C++ Решение и ответ на вопрос 2907665

pgb

-129 / 9 / 0

Регистрация: 22.09.2015

Сообщений: 432

1

23.11.2021, 22:29. Показов 1161. Ответов 7

Метки нет (Все метки)


Есть свой класс через шаблон для разных типов: template <typename dTYPE> class Array {}
Если в main создаю объект класса Array<LETTER> str; то всё нормально.
Если в свойстве другого класса указываю,

C++
1
2
3
4
5
6
class Text {
private:
    Array<LETTER> str;// << ошибка
public:
...
}

то ошибка:

Код

error: specialization of 'Array<dTYPE>::Array() [with dTYPE = char]' after instantiation
 Array<LETTER>::Array(){this->type="LETTER";}
                      ^

В чём дело?

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



0



610 / 415 / 151

Регистрация: 11.01.2019

Сообщений: 1,746

23.11.2021, 22:35

2

Значит, что шаблон уже был инстанцирован для типа char. Специализировать шаблон нельзя после инстанцирования. Ищи место в коде, где была точка инстанцирования.



0



pgb

-129 / 9 / 0

Регистрация: 22.09.2015

Сообщений: 432

23.11.2021, 22:43

 [ТС]

3

Цитата
Сообщение от jugu
Посмотреть сообщение

где была точка инстанцирования

Есть описание класса в заголовке .h, есть описание методов в .cpp
это вот тоже поддчёркивает

C++
1
2
template <>
Array<LETTER>::Array(){this->type="LETTER";}

Не пойму в чём проблема



0



TheCalligrapher

Вездепух

Эксперт CЭксперт С++

10435 / 5704 / 1553

Регистрация: 18.10.2014

Сообщений: 14,101

23.11.2021, 23:27

4

Цитата
Сообщение от pgb
Посмотреть сообщение

Есть описание класса в заголовке .h, есть описание методов в .cpp

Почему описание методов оказалось в .cpp? Зачем было сделано разделение на .h и .cpp?

Цитата
Сообщение от pgb
Посмотреть сообщение

это вот тоже поддчёркивает

C++
1
2
template <>
Array<LETTER>::Array(){this->type="LETTER";}

Так а что это такое? Для чего выполнена эта специализация? Зачем?



0



pgb

-129 / 9 / 0

Регистрация: 22.09.2015

Сообщений: 432

24.11.2021, 09:37

 [ТС]

5

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Почему описание методов оказалось в .cpp? Зачем было сделано разделение на .h и .cpp?

Для подключения и упрощения компиляции, а также работы IDE(вывода свойств объектов и тд.) так делается.

Вот файл Variables.h

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <iostream>
 
using namespace std;
 
//Variables
using POINTER=void*;        // указатель
using LOGIC=bool;           // 0..1
using RANGE=int8_t;     // -128..127
using LETTER=char;      // буква
using BYTE=uint8_t;         // 0..255
using INT_S=int16_t;        // -32768..32767
using INT_W=uint16_t;   // 0..65535
using INT_M=int32_t;        // -2147483648..2147483647
using INT_L=uint32_t;       // 0..4294967295
using INT_T=int64_t;        // -9223372036854775808..9223372036854775807
using INT_B=uint64_t;       // 0..18446744073709551615
using FLOAT=float;          // 1.8E-38..1.8E+38
using DOUBLE=double;    // 2.2E-308..1.8E+308
 
struct Bits {
    unsigned b0:1;
    unsigned b1:1;
    unsigned b2:1;
    unsigned b3:1;
    unsigned b4:1;
    unsigned b5:1;
    unsigned b6:1;
    unsigned b7:1;
};
struct Bytes {
    BYTE b0;
    BYTE b1;
    BYTE b2;
    BYTE b3;
    BYTE b4;
    BYTE b5;
    BYTE b6;
    BYTE b7;
};
 
union Code {
    BYTE byte;
    Bits bit;
};
union Block {
    uint64_t data;
    Bytes byte;
};
 
template <typename dTYPE>
class Array {
private:
    dTYPE *vars;
    STRING type;
    INT_L it=0,sz=0,tl=0,rv=0;
    void Vindex(INT_L &index);
public:
    Array();
    ~Array();
    INT_L Size();
    INT_L Total();
    LOGIC Prev();
    LOGIC Next();
    INT_L Index();
    void Index(INT_L ct);
    dTYPE Get(INT_L index=0);
    void Put(dTYPE val,INT_L index=0);
    void Set(dTYPE val,INT_L index=0);
    void Init(INT_L size=1,INT_L reserve=0);
    void Print(){// здесь реализация временная пока тестирую
        cout << this->type << "(" << this->sz << "," << this->tl << ")" << endl;
        for (INT_L nx=0;nx<this->sz;nx++)
            cout << nx+1 <<  " = " << this->vars[nx] << endl;
    }
};
 
class Text {
private:
    // Если раскомментировать, то будет ошибка
    //Array<LETTER> str; // РАЗКОММЕНТИРОВАТЬ
public:
    Text(const LETTER str[],INT_L rv=0){// здесь реализация временная пока тестирую
        INT_L sz=0;
        while(str[sz]!='')sz++;
        //cout << this->str.Type() << endl;
        //this->str.Init(sz,rv);
        //this->str.Print();
    }
    //~Object() ;
};

Вот файл Variables.cpp

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include "Variables.h"
 
template <>
Array<LOGIC>::Array(){this->type="LOGIC";}
template <>
Array<RANGE>::Array(){this->type="RANGE";}
template <> // тут тоже ошибка возникает
Array<LETTER>::Array(){this->type="LETTER";}
template <>
Array<BYTE>::Array(){this->type="BYTE";}
template <>
Array<INT_S>::Array(){this->type="INT_S";}
template <>
Array<INT_W>::Array(){this->type="INT_W";}
template <>
Array<INT_M>::Array(){this->type="INT_M";}
template <>
Array<INT_L>::Array(){this->type="INT_L";}
template <>
Array<INT_T>::Array(){this->type="INT_T";}
template <>
Array<INT_B>::Array(){this->type="INT_B";}
template <>
Array<FLOAT>::Array(){this->type="FLOAT";}
template <>
Array<DOUBLE>::Array(){this->type="DOUBLE";}
 
template <typename dTYPE>
Array<dTYPE>::~Array(){
    delete []this->vars;
}
template <typename dTYPE>
void Array<dTYPE>::Vindex(INT_L &index){
    if(index>this->sz){index=~index+1;
        if(index<this->sz)index=this->sz-index;
            else index=this->sz-1;
    }else if(index>0)index--;
}
template <typename dTYPE>
INT_L Array<dTYPE>::Size(){return this->sz;}
template <typename dTYPE>
INT_L Array<dTYPE>::Total(){return this->tl;}
template <typename dTYPE>
LOGIC Array<dTYPE>::Prev(){
    if(this->it>0){
        this->it--;return true;
    }else return false;
}
template <typename dTYPE>
LOGIC Array<dTYPE>::Next(){
    if(this->it<this->sz-1){
        this->it++;return true;
    }else return false;
}
template <typename dTYPE>
INT_L Array<dTYPE>::Index(){return this->it+1;}
template <typename dTYPE>
void Array<dTYPE>::Index(INT_L it){
    this->Vindex(it);this->it=it;
}
template <typename dTYPE>
dTYPE Array<dTYPE>::Get(INT_L index){
    if(index==0)index=this->it;
    else this->Vindex(index);
    return this->vars[index];
}
 
template <typename dTYPE>
void Array<dTYPE>::Put(dTYPE val,INT_L index){
    this->Vindex(index);this->sz++;
    if(this->sz>this->tl){
        this->tl+=this->rv+1;
        dTYPE *vars=new dTYPE[this->tl] ;
        INT_L nx=0;
        while(nx<index){
            vars[nx]=this->vars[nx];nx++;
        }
        while(nx<this->sz){
            vars[nx+1]=this->vars[nx];nx++;
        }
        delete []this->vars;
        this->vars=vars;
    }else if(index<this->sz){
        for(INT_L nx=this->sz;nx>index;nx--)
            this->vars[nx]=this->vars[nx-1];
    }
    //cout << "Put[" << index+1 << "]=" << val << endl;
    this->vars[index]=val;
    //this->Print();
}
template <typename dTYPE>
void Array<dTYPE>::Set(dTYPE val,INT_L index){
    if(index==0)index=this->it;
    this->vars[index]=val;
}
template <typename dTYPE>
void Array<dTYPE>::Init(INT_L size,INT_L reserve){
    this->sz=size;this->rv=reserve;this->tl=size+reserve;
    this->vars=new dTYPE[this->tl];
}
 
int main(){
    setlocale(LC_ALL,"");
    Array<INT_L> obj;
    obj.Init(2,3);
    INT_L nx=1;
    do{
        obj.Set(nx);nx++;
    }while(obj.Next());
    obj.Put(nx,2);obj.Put(33,2);obj.Put(44,0);obj.Put(99,-1);
    obj.Put(888,2);obj.Put(5555,5);obj.Put(5555,7);obj.Put(5555,3);
    obj.Print();
    cout << "----------------" << endl;
    if(obj.Size()){
        obj.Index(-1);
        do{
            cout << obj.Get() << endl;
        }while(obj.Prev());
    }
    cout << "----------------" << endl;
    Array<LETTER> str;
    str.Init(2,3);
    str.Set('Ф');
    str.Print();
    
    // Для создания объекта типа Text
    // Text txt("проверка12"); // РАЗКОММЕНТИРОВАТЬ
    
    system("pause");
    return 0;
}

Если разкомментировать строки где есть ещё такой коммент // РАЗКОММЕНТИРОВАТЬ,
то возникает ошибка. Не понимаю в чём дело и как исправить.

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

C++
1
2
template <> // тут тоже ошибка возникает
Array<LETTER>::Array(){this->type="LETTER";}



0



16495 / 8988 / 2205

Регистрация: 30.01.2014

Сообщений: 15,611

24.11.2021, 10:02

6

Цитата
Сообщение от pgb
Посмотреть сообщение

Не понимаю в чём дело и как исправить.

Вам в общем-то довольно ясно написал компилятор в чем дело:

Цитата
Сообщение от pgb
Посмотреть сообщение

specialization of ‘Array<dTYPE>::Array() [with dTYPE = char]’ after instantiation

Вы запросили инстанцирование базового шаблона для типа LETTER, а потом только предоставили специализации для него.
Надо сделать наоборот. Специализации должны быть выше места инстанцирования (если все это происходит в одной единице трансляции).

Цитата
Сообщение от pgb
Посмотреть сообщение

Если в main создаю объект класса Array<LETTER> str; то всё нормально.

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



1



-129 / 9 / 0

Регистрация: 22.09.2015

Сообщений: 432

24.11.2021, 10:16

 [ТС]

7

DrOffset, Спасибо, теперь всё понятно.



0



TheCalligrapher

Вездепух

Эксперт CЭксперт С++

10435 / 5704 / 1553

Регистрация: 18.10.2014

Сообщений: 14,101

24.11.2021, 11:33

8

Цитата
Сообщение от pgb
Посмотреть сообщение

Для подключения и упрощения компиляции, а также работы IDE(вывода свойств объектов и тд.) так делается.

Не понял. Для какого «подключения и упрощения компиляции»? И при чем здесь «работы IDE»?

Нет, с шаблонным кодом так НЕ делается. Где вы такое видели? Все определения шаблонных сущностей должны сидеть во включаемом файле, то есть в вашем случае в .h файле.

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

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

C++
1
2
3
template <> Array<LETTER>::Array();
template <> Array<INT_L>::Array();
// И т.д.

Намного проще было бы определить все ваши специализации inline и поместить все определения в .h файл. Тогда .cpp файла не понадобилось бы вообще.



1



In file included from RMA/RMA_benchmark.cpp:61:
In file included from helpers/original_benchmark.h:58:
../src_c/IMB_prototypes.h:650:22: warning: ‘register’ storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register]
long IMB_compute_crc(register char* buf, register size_t size);
^~~~~~~~~
../src_c/IMB_prototypes.h:650:42: warning: ‘register’ storage class specifier is deprecated and incompatible with C++17 [-Wdeprecated-register]
long IMB_compute_crc(register char* buf, register size_t size);
^~~~~~~~~
When attempting to build RMA on a cray machine at LLNL I get the errors below. The compiler is clang. Is there a fix or workaround for this issue?

zwhamo2{dinge1}47: module list

Currently Loaded Modules:

  1. cpe-cray 2) cce/11.0.3 3) craype/2.7.5 4) craype-x86-rome 5) craype-network-infiniband 6) cray-mvapich2_nogpu/2.3.5 7) cray-libsci/20.03.1 8) perftools-base/21.02.0 9) PrgEnv-cray/1.0.0 10) rocm/4.0.1

rzwhamo2{dinge1}49: which CC
/opt/cray/pe/craype/2.7.5/bin/CC
rzwhamo2{dinge1}50: echo $CXX
CC
rzwhamo2{dinge1}51: CC —version
Cray clang version 11.0.3 (477c94a197f0fb1c961670c6e69c34a212c8f345)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/cray/pe/cce/11.0.3/cce-clang/x86_64/share/../bin

RMA/RMA_benchmark.cpp:79:1: error: explicit specialization of ‘name’ after instantiation
BENCHMARK(IMB_rma_single_put, Unidir_put)
^
RMA/RMA_benchmark.cpp:73:106: note: expanded from macro ‘BENCHMARK’
#define BENCHMARK(BMRK_FN, BMRK_NAME) template class OriginalBenchmark<BenchmarkSuite<BS_RMA>, BMRK_FN>;
^
./benchmark.h:88:114: note: expanded from macro ‘
DECLARE_INHERITED_TEMPLATE’
#define DECLARE_INHERITED_TEMPLATE(CLASS, NAME) namespace { CLASS elem_ ## NAME; } template<> const char *CLASS::name = #NAME;
^
helpers/original_benchmark.h:139:34: note: implicit instantiation first required here
BMark->name = strdup(name);
^
RMA/RMA_benchmark.cpp:79:1: error: explicit specialization of ‘descr’ after instantiation
BENCHMARK(IMB_rma_single_put, Unidir_put)

  • Forum
  • General C++ Programming
  • What’s the point of this error?

What’s the point of this error?

1
2
3
4
5
6
7
8
9
10
11
struct Outer
{
	template<typename T>
	struct Inner
	{
	};
	template<>
	struct Inner<double>
	{
	};
};
prog.cpp:9:11: error: explicit specialization in
                      non-namespace scope ‘struct Outer’
  template<>
           ^

http://ideone.com/sxyQBg
What’s the point of the error when you can just get around it?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Outer
{
	template<typename T, typename = void>
	struct Inner
	{
	};
	template<typename T>
	struct Inner
	<
		typename std::enable_if
		<
			std::is_same<T, double>::value,
			T
		>::type,
		T
	>
	{
	};
};

http://ideone.com/IThuo2

What’s the point of the error when you can just get around it?

The error makes the compiler compliant with the standard?

http://ideone.com/XxOvH2

@JLBorges: Ah, that makes a little sense. Thanks!

@cire: unfortunately that’s not an option because later in the class Outer, the specializations of Inner need to exist to be used.

Last edited on

@cire: unfortunately that’s not an option because later in the class Outer, the specializations of Inner need to exist to be sued.

Is that really an issue?

http://ideone.com/X5dZvg

> That’s not very hard to avoid.

This will not avoid the error, if «in the class Outer, the specializations of Inner need to exist to be used.»:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Outer
{
    template < typename U > struct Inner {} ;

    Inner<int> a ;
    Inner<double> b ;
};

// *** error: specialization of 'Outer::Inner<double>' after instantiation|
template<> struct Outer::Inner<double> {};

int main()
{
	Outer o;
}

This will:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct Outer
{
    template < typename U, typename = void > struct Inner {} ;
    template < typename VOID > struct Inner<double,VOID> {};

    Inner<int> a ;
    Inner<double> b ;
};

int main()
{
	Outer o;
}

JLBorges wrote:
3
4
template < typename U, typename = void > struct Inner {} ;
template < typename VOID > struct Inner<double,VOID> {};

Is this equivalent to my enable_if version? if so, I’ll switch to this more terse version instead.

Last edited on

> Is this equivalent to my enable_if version?

Yes.

Both are technically partial specializations (which are allowed because they are subject to two phase name lookup, with dependant names resolved later during phase two).

Topic archived. No new replies allowed.

I have the following code:

typedef vector<int> Vec;
typedef vector<Vec> VecOfVec;

template<typename Vec>
Vec DoSomething(const Vec &v);

template<>
VecOfVec DoSomething<VecOfVec>(const VecOfVec &v)
{
    VecOfVec r;
    for(auto i = v.begin(); i != v.end(); i++)
        r.push_back(DoSomething(*i));
    return r;
}

template<>
Vec DoSomething<Vec>(const Vec &v) // Error here
{
    return v; // for the sake of the example
}

I get the following error:

explicit specialization of 'DoSomething<vector<int> >' after instantiation

at the marked line.
The compiler insists that it already instantiated DoSomething<vector<int> >, while it cannot, and a simple program can prove it:

typedef vector<int> Vec;
typedef vector<Vec> VecOfVec;

template<typename Vec>
Vec DoSomething(const Vec &v);

template<>
VecOfVec DoSomething<VecOfVec>(const VecOfVec &v)
{
    VecOfVec r;
    for(auto i = v.begin(); i != v.end(); i++)
        r.push_back(DoSomething(*i));
    return r;
}

Results in unresolved external.
Why is the compiler saying it already instantiated it when it cannot and even does not? and why doesn’t the compiler treat it as unresolved symbol, while the linker does? I know switching the method order solves it, but I want to know why is the compiler doing it.


The code requested an implicit instantiation at DoSomething(*i). The fact that you didn’t define the template in that translation unit means that it could not instantiate a specialization, hence DoSomething(*i) yields an «unresolved symbol» (linker-) error in your case. To get rid of that error, you either have to define the template in that TU, or provide an explicit instantiation directive of that template in a TU where you define the template.

The mere fact that the code requested an implicit instantiation for specialization DoSomething<vector<int> > before you explicitly provided that specialization is enough for the program to become ill-formed (without a diagnostic being required though; the compiler does a good job here which it is not required to do).

As @CharlesBailey helpfully notes, declaring the explicit specialization is perfectly sufficient; a definition of it can be given elsewhere, even outside of the using TU.

Мой полный код слишком длинный, но вот фрагмент, который отражает суть моей проблемы:

class BPCFGParser {
  public:

  ...
  ...

  class Edge {
    ...
    ...
  };


  class ActiveEquivClass {
    ...
    ...
  };

  class PassiveEquivClass {
    ...
    ...
  };

  struct EqActiveEquivClass {
    ...
    ...
  };

  struct EqPassiveEquivClass {
    ...
    ...
  };



  unordered_map<ActiveEquivClass, Edge *, hash<ActiveEquivClass>, EqActiveEquivClass> discovered_active_edges;
  unordered_map<PassiveEquivClass, Edge *, hash<PassiveEquivClass>, EqPassiveEquivClass> discovered_passive_edges;

};

namespace std {


template <>
class hash<BPCFGParser::ActiveEquivClass>
{

    public:
        size_t operator()(const BPCFGParser::ActiveEquivClass & aec) const {

        }
};

template <>
class hash<BPCFGParser::PassiveEquivClass>
{

    public:
        size_t operator()(const BPCFGParser::PassiveEquivClass & pec) const {

        }
};

}

Когда я компилирую этот код, я получаю следующие ошибки:

In file included from BPCFGParser.cpp:3,
                 from experiments.cpp:2:
BPCFGParser.h:408: error: specialization of ‘std::hash<BPCFGParser::ActiveEquivClass>’     after instantiation
BPCFGParser.h:408: error: redefinition of ‘class                 std::hash<BPCFGParser::ActiveEquivClass>’
/usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::ActiveEquivClass>’
BPCFGParser.h:445: error: specialization of     ‘std::hash<BPCFGParser::PassiveEquivClass>’ after instantiation
BPCFGParser.h:445: error: redefinition of ‘class std::hash<BPCFGParser::PassiveEquivClass>’
/usr/include/c++/4.3/tr1_impl/functional_hash.h:44: error: previous definition of     ‘class std::hash<BPCFGParser::PassiveEquivClass>’

Теперь мне нужно специализировать std :: hash для этих классов (поскольку стандартное определение std :: hash не включает типы, определяемые пользователем). Когда я перемещаю эти специализации шаблона перед определением класса BPCFGParser, Я получаю множество ошибок из-за множества разных вещей, которые пробовал, и где-то (http://www.parashift.com/c++-faq-lite/misc-technical-issues.html) Я прочитал это:

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

Так что я застрял. Я не могу специализировать шаблоны после BPCFGParser определение, я не могу специализировать их раньше BPCFGParser определение, как я могу заставить это работать?


Вам нужно переместить специализацию во внутренний класс внутри BPCFGParser. Это соответствует обоим требованиям.

Большое спасибо за ответ :)

hash класс определяется в пространстве имен std. Это не позволяет мне специализировать шаблоны для hash в области, не относящейся к пространству имен. Даже следующее:

template <>
  class std::hash<ActiveEquivClass> {
...

не работал. Когда я заключаю специализации с namespace std {}однако это дает странную ошибку:

In file included from BPCFGParser.cpp:3,
                 from experiments.cpp:2:
BPCFGParser.h:225: error: expected unqualified-id before ‘namespace’
experiments.cpp:7: error: expected `}' at end of input
BPCFGParser.h:222: error: expected unqualified-id at end of input

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

Allows customizing the template code for a given set of template arguments.

Contents

  • 1 Syntax
  • 2 In detail
  • 3 Explicit specializations of function templates
  • 4 Members of specializations
  • 5 Defect reports
  • 6 See also

[edit] Syntax

template <> declaration

Any of the following can be fully specialized:

  1. function template
  2. class template
  3. variable template (since C++14)
  4. member function of a class template
  5. static data member of a class template
  6. member class of a class template
  7. member enumeration of a class template
  8. member class template of a class or class template
  9. member function template of a class or class template

For example,

#include <iostream>
 
template<typename T> // primary template
struct is_void : std::false_type {};
template<>           // explicit specialization for T = void
struct is_void<void> : std::true_type {};
 
int main()
{
    // for any type T other than void, the class is derived from false_type
    std::cout << is_void<char>::value << 'n'; 
    // but when T is void, the class is derived from true_type
    std::cout << is_void<void>::value << 'n';
}

[edit] In detail

Explicit specialization may be declared in any scope where its primary template may be defined (which may be different from the scope where the primary template is defined; such as with out-of-class specialization of a member template) . Explicit specialization has to appear after the non-specialized template declaration.

namespace N
{
    template<class T> // primary template
    class X { /*...*/ };
    template<>        // specialization in same namespace
    class X<int> { /*...*/ };
 
    template<class T> // primary template
    class Y { /*...*/ };
    template<>        // forward declare specialization for double
    class Y<double>;
}
 
template<> // OK: specialization in same namespace
class N::Y<double> { /*...*/ };

Specialization must be declared before the first use that would cause implicit instantiation, in every translation unit where such use occurs:

class String {};
 
template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v) { /*...*/ }
 
void f(Array<String>& v)
{
    sort(v); // implicitly instantiates sort(Array<String>&), 
}            // using the primary template for sort()
 
template<> // ERROR: explicit specialization of sort(Array<String>)
void sort<String>(Array<String>& v); // after implicit instantiation

A template specialization that was declared but not defined can be used just like any other incomplete type (e.g. pointers and references to it may be used):

template<class T> // primary template
class X;
template<>        // specialization (declared, not defined)
class X<int>;
 
X<int>* p; // OK: pointer to incomplete type
X<int> x;  // error: object of incomplete type

[edit] Explicit specializations of function templates

When specializing a function template, its template arguments can be omitted if template argument deduction can provide them from the function arguments:

template<class T>
class Array { /*...*/ };
 
template<class T> // primary template
void sort(Array<T>& v);
template<>        // specialization for T = int
void sort(Array<int>&);
 
// no need to write
// template<> void sort<int>(Array<int>&);

A function with the same name and the same argument list as a specialization is not a specialization (see template overloading in function template)

An explicit specialization of a function template is inline/constexpr (since C++11)/immediate (since C++20) only if it is declared with the corresponding specifier (or defined as deleted) (since C++11), it does not matter if the primary template is declared with that specifier. Similarly, attributes appearing in the declaration of a template have no effect on an explicit specialization of that template: (since C++11)

template<class T>
void f(T) { /* ... */ }
template<>
inline void f<>(int) { /* ... */ } // OK, inline
 
template<class T>
inline T g(T) { /* ... */ }
template<>
int g<>(int) { /* ... */ }         // OK, not inline
 
template<typename>
[[noreturn]] void h([[maybe_unused]] int i);
template<> void h<int>(int i)
{
    // [[noreturn]] has no effect, but [[maybe_unused]] has
}

Default function arguments cannot be specified in explicit specializations of function templates, member function templates, and member functions of class templates when the class is implicitly instantiated.

An explicit specialization cannot be a friend declaration.

[edit] Members of specializations

When defining a member of an explicitly specialized class template outside the body of the class, the syntax template<> is not used, except if it’s a member of an explicitly specialized member class template, which is specialized as a class template, because otherwise, the syntax would require such definition to begin with template<parameters> required by the nested template

template<typename T>
struct A
{
    struct B {};      // member class 
 
    template<class U> // member class template
    struct C {};
};
 
template<> // specialization
struct A<int> 
{
    void f(int); // member function of a specialization
};
// template<> not used for a member of a specialization
void A<int>::f(int) { /* ... */ }
 
template<> // specialization of a member class
struct A<char>::B
{
    void f();
};
// template<> not used for a member of a specialized member class either
void A<char>::B::f() { /* ... */ }
 
template<> // specialization of a member class template
template<class U>
struct A<char>::C
{
    void f();
};
 
// template<> is used when defining a member of an explicitly
// specialized member class template specialized as a class template
template<>
template<class U>
void A<char>::C<U>::f() { /* ... */ }

An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. These definitions must use braces for default initialization:

template<>
X Q<int>::x;    // declaration of a static member
template<>
X Q<int>::x (); // error: function declaration
template<>
X Q<int>::x {}; // definition of a default-initialized static member

A member or a member template of a class template may be explicitly specialized for a given implicit instantiation of the class template, even if the member or member template is defined in the class template definition.

template<typename T>
struct A
{
    void f(T);         // member, declared in the primary template
 
    void h(T) {}       // member, defined in the primary template
 
    template<class X1> // member template
    void g1(T, X1);
 
    template<class X2> // member template
    void g2(T, X2);
};
 
// specialization of a member
template<>
void A<int>::f(int);
 
// member specialization OK even if defined in-class
template<>
void A<int>::h(int) {}
 
// out of class member template definition
template<class T>
template<class X1>
void A<T>::g1(T, X1) {}
 
// member template specialization
template<>
template<class X1>
void A<int>::g1(int, X1);
 
// member template specialization
template<>
template<>
void A<int>::g2<char>(int, char); // for X2 = char
 
// same, using template argument deduction (X1 = char)
template<> 
template<>
void A<int>::g1(int, char);

Member or a member template may be nested within many enclosing class templates. In an explicit specialization for such a member, there’s a template<> for every
enclosing class template that is explicitly specialized.

template<class T1>
struct A
{
    template<class T2>
    struct B
    {
        template<class T3>
        void mf();
    };
};
 
template<>
struct A<int>;
 
template<>
template<>
struct A<char>::B<double>;
 
template<>
template<>
template<>
void A<char>::B<char>::mf<double>();

In such a nested declaration, some of the levels may remain unspecialized (except that it can’t specialize a class member template if its enclosing class is unspecialized). For each of those levels, the declaration needs template<arguments>, because such specializations are themselves templates:

template<class T1>
class A
{
    template<class T2>
    class B
    {
        template<class T3> // member template
        void mf1(T3);
 
        void mf2();        // non-template member
    };
};
 
// specialization
template<>        // for the specialized A
template<class X> // for the unspecialized B
class A<int>::B
{
    template<class T>
    void mf1(T);
};
 
// specialization
template<>        // for the specialized A
template<>        // for the specialized B
template<class T> // for the unspecialized mf1
void A<int>::B<double>::mf1(T t) {}
 
// ERROR: B<double> is specialized and is a member template, so its enclosing A
// must be specialized also
template<class Y>
template<>
void A<Y>::B<double>::mf2() {}

[edit] Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 531 C++98 the syntax of defining members of explicit
specializations in namespace scope was not specified
specified
CWG 727 C++98 full specializations not allowed in
class scope, even though partial are
full specialization
allowed in any scope
CWG 730 C++98 member templates of non-template
classes could not be fully specialized
allowed
CWG 2604 C++11 it was unclear whether the attributes of the primary
template are carried over into its explicit specializations
not carried over

[edit] See also

  • templates
  • class template
  • function template
  • partial specialization

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

#include <iostream>

template<typename T>
const char* templateImpl();

template<typename T>
const char* templateGetter() { return templateImpl<T>(); }

struct S{};

int main(){ std::cout << templateGetter<S>() << std::endl; return 0; }

template<>
const char* templateImpl<S>(){ return "S"; }

Это работает с каждым компилятором — я не удивлен, что MSVC компилирует его, так как он обрабатывает шаблоны по-разному, но GCC и clang также позволяют это. Я думал, что стандарт требует, чтобы специализация произошла до первого использования, что в данном случае означало бы перед основным, и ожидал, что они сообщат об ошибке.

Я что-то пропустил, соответствует ли этот стандарт стандарту?

Чтобы уточнить, если я изменю templateGetter<S> в templateImpl<S> в основном, программа не будет компилироваться с сообщением об ошибке, которое я ожидаю от этого:

main.cpp: 14: 29: ошибка: специализация ‘const char * templateImpl ()
[с T = S] ‘после создания

14

Решение

Вам повезло (не) Это плохо сформированный NDR.

[Temp.expl.spec] / 6-7:

6 Если шаблон, шаблон элемента или элемент шаблона класса
явно специализированный тогда эта специализация должна быть объявлена
до первого использования этой специализации, что вызвало бы
неявное создание экземпляра в каждой единице перевода в
какое такое использование происходит
; Диагностика не требуется. […]

7 Размещение явных объявлений специализации для шаблонов функций, шаблонов классов, шаблонов переменных, функций-членов шаблонов классов, статических данных-членов шаблонов классов, классов-членов шаблонов классов, перечислений членов-шаблонов классов, шаблонов классов-членов шаблонов классов, функции-члена шаблоны шаблонов классов, шаблоны-члены статических данных шаблонов классов, функции-члены шаблонов-членов шаблонов классов, функции-члены шаблонов-членов не шаблонных классов, шаблоны-члены статических данных не-шаблонных классов, шаблоны функций-членов классов-членов шаблоны классов и т. д., а также размещение объявлений частичной специализации шаблонов классов, шаблонов переменных, шаблонов классов-членов классов, не являющихся шаблонами, шаблонов элементов статических данных не-шаблонных классов, шаблонов классов-членов шаблонов классов и т. д. влияет на правильность формирования программы в соответствии с относительным положением явного вида декларации о соответствии и их пункты в модуле перевода, как указано выше и ниже. При написании специализации будьте осторожны с ее местоположением; или сделать его компиляцией будет таким испытанием, чтобы разжечь его самосожжение.

p7 здесь не очень полезен, но я не могу удержаться от цитирования 🙂

Инстанцирование templateGetter<S> вызывает неявное создание декларации templateImpl<S>, Вы не видели ошибку в своем коде, потому что многие реализации любят откладывать создание экземпляров шаблона до конца модуля перевода, когда это возможно, что является допустимым методом реализации. (Я не собираюсь цитировать стандартное здесь, но вы обнаружите, что специализации шаблонов функций имеют дополнительную точку создания в конце модуля перевода.)

дающий templateGetter выведенный тип возврата будет вызывать раннее создание его тела:

template<typename T>
auto templateGetter() { return templateImpl<T>(); }

а также вуаля:

+ g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:29: error: specialization of 'const char* templateImpl() [with T = S]' after instantiation
const char* templateImpl<S>(){ return "S"; }
^
+ clang++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp
main.cpp:14:13: error: explicit specialization of 'templateImpl<S>' after instantiation
const char* templateImpl<S>(){ return "S"; }
^
main.cpp:7:32: note: implicit instantiation first required here
auto templateGetter() { return templateImpl<T>(); }
^
1 error generated.

10

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

Я думаю, что это законно. Цитируя N4140, [temp.point]:

Для специализации шаблона функции, … если специализация
создается неявно, потому что на него ссылаются из другого
специализация шаблона и контекст, на который он ссылается
зависит от параметра шаблона, точка создания
специализация является точкой воплощения вложения
специализация. В противном случае, точка инстанции для такого
специализация сразу следует за объявлением области имен пространства или
определение, которое относится к специализации.

Затем в [temp.fct.spec]:

Функция, созданная из шаблона функции, называется функцией
специализация шаблона; так явная специализация
шаблон функции. …

Другими словами, ошибка на самом деле не произойдет, пока templateGetter() и впоследствии специализация templateImpl создаются, как вы ожидаете, шаблоны будут работать.

1

Понравилась статья? Поделить с друзьями:
  • Error spawn run exe sirus
  • Error spawn node enoent
  • Error setting certificate verify locations visual studio
  • Error spawn ffmpeg enoent
  • Error setting certificate verify locations cafile etc pki tls certs ca bundle crt capath none