not необходимо поместить реализацию в файл заголовка, см. альтернативное решение в конце этого ответа.
В любом случае причина, по которой ваш код не работает, что при создании экземпляра шаблона компилятор создает новый класс с заданным аргументом шаблона. Например:
template
struct Foo
{
T bar;
void doSomething(T param) {/* do stuff using T */}
};
// somewhere in a .cpp
Foo f;
При чтении этой строки компилятор создаст новый класс (назовем его FooInt
), что эквивалентно следующему:
struct FooInt
{
int bar;
void doSomething(int param) {/* do stuff using int */}
}
Следовательно, компилятор должен иметь доступ к реализации методов, чтобы создать экземпляр с аргументом шаблона (в данном случае int
). Если эти реализации не были в заголовке, они не были бы доступны, поэтому компилятор не смог бы создать экземпляр шаблона.
Общим решением для этого является запись объявления шаблона в заголовок файла, затем реализовать класс в файле реализации (например, .tpp) и включить этот файл реализации в конец заголовка.
// Foo.h
template
struct Foo
{
void doSomething(T param);
};
#include "Foo.tpp"
// Foo.tpp
template
void Foo::doSomething(T param)
{
//implementation
}
Таким образом, реализация по-прежнему отделена от объявления, но доступен компилятору.
Другое решение состоит в том, чтобы сохранить реализацию отдельно и явно создать все экземпляры шаблона, которые вам понадобятся:
// Foo.h
// no implementation
template struct Foo { ... };
//----------------------------------------
// Foo.cpp
// implementation of Foo's methods
// explicit instantiations
template class Foo;
template class Foo;
// You will only be able to use Foo with int or float
Если мое объяснение isn ' t достаточно ясно, вы можете взглянуть на C ++ Super-FAQ по этому вопросу .
Я обычно использую союзы при разборе текста. Я использую нечто подобное:
typedef enum DataType { INTEGER, FLOAT_POINT, STRING } DataType ;
typedef union DataValue
{
int v_int;
float v_float;
char* v_string;
}DataValue;
typedef struct DataNode
{
DataType type;
DataValue value;
}DataNode;
void myfunct()
{
long long temp;
DataNode inputData;
inputData.type= read_some_input(&temp);
switch(inputData.type)
{
case INTEGER: inputData.value.v_int = (int)temp; break;
case FLOAT_POINT: inputData.value.v_float = (float)temp; break;
case STRING: inputData.value.v_string = (char*)temp; break;
}
}
void printDataNode(DataNode* ptr)
{
printf("I am a ");
switch(ptr->type){
case INTEGER: printf("Integer with value %d", ptr->value.v_int); break;
case FLOAT_POINT: printf("Float with value %f", ptr->value.v_float); break;
case STRING: printf("String with value %s", ptr->value.v_string); break;
}
}
Если вы хотите посмотреть, как используются союзы ЗДЕСЬ, проверьте любой код, используя flex/bison. Например, см. splint, он содержит ТОНЫ союзов.
.Союзы полезны, если у вас разные типы сообщений, и в этом случае вам не обязательно знать на каких-то промежуточных уровнях точный тип. Только отправитель и получатель должны разобрать фактическое сообщение сообщения. Любые другие уровни действительно должны знать только размер и, возможно, информацию об отправителе и/или получателе.
.Унионы также могут быть полезны, когда тип перфорации, что желательно в некоторых избранных местах (например, некоторые методы для сравнения с плавающей точкой алгоритмов).
.Для удобства я использую союзы, чтобы использовать тот же класс для хранения значений xyzw и rgba
#ifndef VERTEX4DH
#define VERTEX4DH
struct Vertex4d{
union {
double x;
double r;
};
union {
double y;
double g;
};
union {
double z;
double b;
};
union {
double w;
double a;
};
Vertex4d(double x=0, double y=0,double z=0,double w=0) : x(x), y(y),z(z),w(w){}
};
#endif
Многие примеры союзов можно найти в
. Немногие другие находятся в некоторых IP-стеках (например, в BSD
).
Как правило, реализации протоколов используют союзную конструкцию.
.Другой пример больше: чтобы сэкономить на выполнении кастингов.
typedef union {
long int_v;
float float_v;
} int_float;
void foo(float v) {
int_float i;
i.float_v = v;
printf("sign=%d exp=%d fraction=%d", (i.int_v>>31)&1, ((i.int_v>>22)&0xff)-128, i.int_v&((1<<22)-1));
}
вместо:
void foo(float v) {
long i = *((long*)&v);
printf("sign=%d exp=%d fraction=%d", (i>>31)&1, ((i>>22)&0xff)-128, i&((1<<22)-1));
}
Пример:
При использовании различных типов сокетов, но вы хотите, чтобы на них ссылался тип comon.
.Вы имеете в виду что-то подобное ?
union {
long long a;
unsigned char b[sizeof(long long)];
} long_long_to_single_bytes;
ADDED:
Я недавно использовал это на нашей машине AIX для преобразования 64-битного машиноиндентификатора в байт-массив.
std::string getHardwareUUID(void) {
#ifdef AIX
struct xutsname m; // aix specific struct to hold the 64bit machine id
unamex(&b); // aix specific call to get the 64bit machine id
long_long_to_single_bytes.a = m.longnid;
return convertToHexString(long_long_to_single_bytes.b, sizeof(long long));
#else // Windows or Linux or Solaris or ...
... get a 6byte ethernet MAC address somehow and put it into mac_buf
return convertToHexString(mac_buf, 6);
#endif
Иногда я использовал объединения таким образом
//Define type of structure
typedef enum { ANALOG, BOOLEAN, UNKNOWN } typeValue_t;
//Define the union
typedef struct {
typeValue_t typeValue;
/*On this structure you will access the correct type of
data according to its type*/
union {
float ParamAnalog;
char ParamBool;
};
} Value_t;
Тогда можно было объявить массивы с разными значениями, более или менее эффективно хранящие данные, и выполнить некоторые "полиморфные" операции типа:
void printValue ( Value_t value ) {
switch (value.typeValue) {
case BOOL:
printf("Bolean: %c\n", value.ParamBool?'T':'F');
break;
case ANALOG:
printf("Analog: %f\n", value.ParamAnalog);
break;
case UNKNOWN:
printf("Error, value UNKNOWN\n");
break;
}
}
struct cat_info
{
int legs;
int tailLen;
};
struct fish_info
{
bool hasSpikes;
};
union
{
fish_info fish;
cat_info cat;
} animal_data;
struct animal
{
char* name;
int animal_type;
animal_data data;
};
В мире Windows объединения
обычно используются для реализации вариантов с метками , которые являются (или были до .NET?) одним из стандартных способов передачи данных между COM объектами.
Идея заключается в том, что тип объединения
может обеспечить единый естественный интерфейс для передачи произвольных данных между двумя объектами. Некоторые COM объекты могут передавать вам вариант (например, тип VARIANT
или _variant_t
), который может содержать либо double
, либо float
, либо int
, либо что угодно.
Если вам приходится иметь дело с COM объектами в коде Windows C++, то вы увидите типы вариантов повсюду.
Для пошагового и битового доступа к регистрам или портам ввода/вывода путём отображения этого конкретного порта в память, смотрите пример ниже:
typedef Union
{
unsigned int a;
struct {
unsigned bit0 : 1,
bit1 : 1,
bit2 : 1,
bit3 : 1,
bit4 : 1,
bit5 : 1,
bit6 : 1,
bit7 : 1,
bit8 : 1,
bit9 : 1,
bit10 : 1,
bit11 : 1,
bit12 : 1,
bit13 : 1,
bit14 : 1,
bit15 : 1
} bits;
} IOREG;
# define PORTA (*(IOREG *) 0x3B)
...
unsigned int i = PORTA.a;//read bytewise
int j = PORTA.bits.bit0;//read bitwise
...
PORTA.bits.bit0 = 1;//write operation
Я обычно использовал союзы, где вы хотите иметь различные представления о данных. Например, 32-битное значение цвета, где вы хотите и 32-битный val, и красный, зеленый, синий и альфа компоненты
struct rgba
{
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
};
union
{
unsigned int val;
rgba components;
}colorval32;
NB То же самое вы могли бы достичь и с помощью битовой маскировки и сдвига, т.е.
#define GETR(val) ((val&0xFF000000) >> 24)
, но я считаю союзный подход более элегантным
.Вот еще один пример, где объединение могло бы быть полезным.
(не моя собственная идея, я нашел это в документе, где обсуждается c++ optimizations)
begin-quote
.... Для экономии места можно использовать союзы, например,
сначала несоюзный подход:
void F3(bool useInt) {
if (y) {
int a[1000];
F1(a); // call a function which expects an array of int as parameter
}
else {
float b[1000];
F2(b); // call a function which expects an array of float as parameter
}
}
Здесь можно использовать одну и ту же область памяти для a и b, так как их живые диапазоны делают а не накладываться друг на друга. Вы можете сэкономить много кэш-памяти , присоединившись к a и b в союзе:
void F3(bool useInt) {
union {
int a[1000];
float b[1000];
};
if (y) {
F1(a); // call a function which expects an array of int as parameter
}
else {
F2(b); // call a function which expects an array of float as parameter
}
}
Использование союза не является безопасной практикой программирования, конечно, потому что вы получите не предупреждение компилятора, если использование a и b пересекаются. Следует использовать только этот метод для больших объектов, которые занимают много места в кэше. ...
endqoute
lex
в yacc
. (yylval
)Недавно я думаю, что видел какой-то союз, используемый в векторных программирования . Векторное программирование используется в технологии INTEL MMX Technology , оборудование GPU, English IBM широкополосный двигатель и другие.
Вектор может соответствовать 128-битному регистре. Это очень часто используется для архитектуры Simd . Поскольку оборудование имеет 128-битные регистры, вы можете хранить 4 одноточных точками в регистре / переменной. Простой способ построить, преобразовывать, экстракт отдельных элементов вектора - использовать объединение.
typedef union {
vector4f vec; // processor-specific built-in type
struct { // human-friendly access for transformations, etc
float x;
float y;
float z;
float w;
};
struct { // human-friendly access for color processing, lighting, etc
float r;
float g;
float b;
float a;
};
float arr[4]; // yet another convenience access
} Vector4f;
int main()
{
Vector4f position, normal, color;
// human-friendly access
position.x = 12.3f;
position.y = 2.f;
position.z = 3.f;
position.w = 1.f;
// computer friendly access
//some_processor_specific_operation(position.vec,normal.vec,color.vec);
return 0;
}
Если вы возьмете путь в PlayStation 3 многоядерных программирования или графическому программированию, хороший шанс вы столкнетесь с большим количеством этих материалов.
SDL использует союз для представления событий: http://www.libsdl.org/cgi/docwiki.cgi/sdl_event .