сбой охранников кода и шаблон из строкового литерала - PullRequest
1 голос
/ 24 июля 2011

Я знаю, что единственный способ передать строковый литерал в качестве аргумента шаблона - объявить его раньше:

файл a.h

#ifndef A_H
#define A_H

#include <string>

char EL[] = "el";


template<char* name>
struct myclass
{
  std::string get_name() { return name; }
};

typedef myclass<EL> myclass_el;

#endif

файл a.cpp

#include "a.cpp"

main.cpp

#include "a.h"
...

g++ -c a.cpp
g++ -c main.cpp
g++ -o main main.o a.o

и я получил:

a.o:(.data+0x0): multiple definition of `EL'
main.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status

Я не могу объявить EL внешним, и я хочу оставить a.cpp. Решения?

Ответы [ 2 ]

3 голосов
/ 24 июля 2011

Давайте начнем с того, что Стандарт говорит для всеобщего блага, с 14.3.2 Типовые аргументы нетипичного типа [temp.arg.nontype] (Стандарт C ++ 03):

1 Шаблон-аргумент для нетипичного, не шаблонного шаблона-параметра должен быть одним из:

- интегральная константа-выражение интеграла или тип перечисления; или

- имя нетипового шаблона-параметра; или

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

- указатель на член выражен как описано в 5.3.1.

Акцент на шахте для соответствующих частей.

Кроме того, в параграфе 5 перечислены допустимые преобразования, и одним из них является затухание указателя массива в указатель. Параграф 2 - даже примечание, которое демонстрирует аналогичное использование char*, как и у OP.

Осталось только получить объект в заголовке с внешней связью и без ошибок. Обычным способом является объявление в заголовке и одно-единственное определение в одном TU.

// In header
extern char EL[]; // array of unspecified size, an incomplete type
                  // extern char EL[3] is acceptable, too.

// In source
char EL[] = "el";

Обратите внимание, что static не представляется возможным из-за требования, чтобы объект имел внешнюю связь. Неименованное пространство имен должно быть предпочтительным, если предполагается, что в TU должен быть отдельный объект.

// In header
// NOT RECOMMENDED! Be wary of ODR-violations with such constructs
// or simply only use these in source files
namespace {

// Recommend using const here, which in turn means using extern
// change non-type template parameter accordingly
extern const char EL[] = "el";

} // namespace

Для любопытных C ++ 0x ослабил требование, чтобы объект имел внешнюю связь, чтобы быть допустимым параметром. (Моя копия GCC пока не поддерживает это.) Строковые литералы необъяснимо все же запрещено отображать в качестве аргументов шаблона.

2 голосов
/ 24 июля 2011

Исправленный ответ (Предыдущий ответ был бессмысленным. Извините за это! Кроме того, ваш предыдущий вопрос уже должен был полностью решить эту проблему.)

Заголовок:

#ifndef H_ABC
#define H_ABC

extern char EL[];

template <const char * S>
struct Foo
{
  static inline const char * get_name() { return S; }
  static const char * name;
};
template <const char * S> const char * Foo<S>::name(S);

typedef Foo<EL> MyElClass;

#endif

Вам нужен один TU для определения EL:

#include "header.h"
char EL[] = "EL";

Шаблон можно использовать где угодно:

#include "header.h"

char A[] = "abc";
extern const char B[] = "xyz";  // must have extern linkage!

void f() {
  std::cout << MyElClass::name << std::endl;
  std::cout << MyElClass::get_name() << std::endl;
  std::cout << Foo<A>::name << std::endl;
  std::cout << Foo<B>::name << std::endl;
}
...