1

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?

Djizeus
  • 4,161
  • 1
  • 24
  • 42
Ruggero Turra
  • 16,929
  • 16
  • 85
  • 141
  • 1
    Why would you want a char-pointer as a template parameter?? What's the real situation here? Template parameters must be compile-time constants of integral types. – Kerrek SB Jul 23 '11 at 23:50
  • You already posted this question. Why accept your previous question if it did not solve your problem? – Marlon Jul 23 '11 at 23:59
  • 1
    @Marlon: because it solves the example problem. – Ruggero Turra Jul 24 '11 at 00:01
  • 1
    @Marlon: This is a new problem. OP: Maybe tell us what you *want*, not what you've *tried*. – Kerrek SB Jul 24 '11 at 00:02
  • @Kerrek SB: why not? Suppose I want a lot of classes when the behaviour of `get_name` is a bit different. By the way, the problem is another. I've problem with the linker. – Ruggero Turra Jul 24 '11 at 00:03
  • But templates aren't the right approach here, because there's no dependence of the class on any external type. So you can just write simple classes: `struct myclass_el { static const std::string = "el"; };` – Kerrek SB Jul 24 '11 at 00:10
  • @Kerrek SB: sorry, but I can't explain everything about my problem, I need to give you an example to reproduce it. So please replay only to the question. How to solve the linker error? – Ruggero Turra Jul 24 '11 at 00:15
  • 1
    See previous question, or say `static const char EL[] = ...`, then every TU will get its own, private copy. But it doesn't matter, because *you cannot have `char*` as a template parameter*! – Kerrek SB Jul 24 '11 at 00:16
  • Hm, did this change from C++98 to C++0x? – Kerrek SB Jul 24 '11 at 00:19
  • @Kerrek, I think you should post the comment about it making `static` as an answer, this seems to be the right solution – littleadv Jul 24 '11 at 00:21
  • @littleadv: Wait, not yet. The point is that it has to work as a template parameter. I'm struggling at the moment, it works in one TU (see my answer below), but not if I have multiple TUs. (I added `static` to my [last answer](http://stackoverflow.com/questions/6803918/code-guards-fail/6803941).) – Kerrek SB Jul 24 '11 at 00:25
  • 3
    Your header guards aren't failing. They make sure a header is included once per code file, not once for all code files. – ssube Jul 24 '11 at 00:47
  • OK, it actually all works just as you planned, and the last answer should already have solved it after all. What I couldn't make work is to make the template value a static member constant, but using the `get_name()` function seems to work. – Kerrek SB Jul 24 '11 at 01:44
  • Hm, even that wasn't true. Now I don't know anymore, it all seems to work. I'll wipe my answer and type it out again, but it was Luc's doing really. – Kerrek SB Jul 24 '11 at 02:01

2 Answers2

3

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.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • Thanks for the quotes from the standard! Now, can you make an example of a template on `char*` that will except your `EL` as a parameter? – Kerrek SB Jul 24 '11 at 01:36
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;
}
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084