问题
I know the only way to pass a string literal as template argument is to declare it before:
file 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
file 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
and I got:
a.o:(.data+0x0): multiple definition of `EL'
main.o:(.data+0x0): first defined here
collect2: ld returned 1 exit status
I can't declare EL
as external and I want to keep the a.cpp
. Solutions?
回答1:
Let's start with what the Standard says for the benefit of all, from 14.3.2 Template non-type arguments [temp.arg.nontype] (C++03 Standard):
1 A template-argument for a non-type, non-template template-parameter shall be one of:
— an integral constant-expression of integral or enumeration type; or
— the name of a non-type template-parameter; or
— the address of an object or function with external linkage, including function templates and function template-ids but excluding non-static class members, expressed as & id-expression where the & is optional if the name refers to a function or array, or if the corresponding template-parameter is a reference; or
— a pointer to member expressed as described in 5.3.1 .
Emphasis mine for the relevant parts.
Additionally, paragraph 5 lists the conversions that are allowed and one of them is array to pointer decay. Paragraph 2 is even a note that showcases a similar use of char*
as that of the OP.
All that is left is how to have an object in a header with external linkage and no errors. The usual way is a declaration in the header, and one and only one definition in one 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";
Note that static
is not a possibility because of the requirement that the object have external linkage. The unnamed namespace is to be preferred if the intent is to have a separate object per 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
For the curious, C++0x relaxed the requirement that an object have external linkage to be a valid parameter. (My copy of GCC doesn't support that yet.) String literals are inexplicably still forbidden to appear as template arguments.
回答2:
Revised Answer (The previous answer was nonsense. Sorry for that! Also, your previous question should have covered this problem already entirely.)
Header:
#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
You need one TU to define EL
:
#include "header.h"
char EL[] = "EL";
You can use the template anywhere:
#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;
}
来源:https://stackoverflow.com/questions/6804029/code-guards-fail-and-template-from-string-literal