Strings as non-type template parameters

R

Rennie deGraaf

Hello,

What are the rules concerning how to use a string as a template
parameter? I assume that the reason why it is necessary to use a named
object rather than a string literal is that the compiler needs to be
able to convert the value into an integer (or something similar), but I
haven't been able to make any sense about how it handles extern and const.

For instance, consider the following code:

typedef const wchar_t Bar[];
//typedef wchar_t Bar[];
//typedef const wchar_t* Bar;
//typedef const wchar_t* const Bar;

extern Bar bar = L"bar";
//Bar bar = L"bar";

template <Bar>
struct Foo {};

Foo<bar> foo;

int main()
{
return 0;
}

When using the first definition of Bar (as a const array) and the first
definition of bar (as extern), it compiles*. When Bar is non-const (the
second definition), then it compiles whether or not bar is extern
(although I get a warning if it /is/ extern). But when I use const and
/not/ extern, it doesn't compile. Why?

Also, why won't it work when I declare Bar as a pointer rather than an
array? I must admit to never having entirely understood the difference
between the two forms of declaration.

Incidentally, why can't I typedef Bar as "extern const wchar_t Bar[]"?

Thanks,
Rennie

* using either GCC 4.4.1 or Microsoft Visual Studio 2008
 
I

Ian Collins

Hello,

What are the rules concerning how to use a string as a template
parameter? I assume that the reason why it is necessary to use a named
object rather than a string literal is that the compiler needs to be
able to convert the value into an integer (or something similar), but I
haven't been able to make any sense about how it handles extern and const.

For instance, consider the following code:

typedef const wchar_t Bar[];
//typedef wchar_t Bar[];
//typedef const wchar_t* Bar;
//typedef const wchar_t* const Bar;

extern Bar bar = L"bar";
//Bar bar = L"bar";

template<Bar>
struct Foo {};

Foo<bar> foo;

int main()
{
return 0;
}

When using the first definition of Bar (as a const array) and the first
definition of bar (as extern), it compiles*. When Bar is non-const (the
second definition), then it compiles whether or not bar is extern
(although I get a warning if it /is/ extern). But when I use const and
/not/ extern, it doesn't compile. Why?

Linkage?

extern consts have global linkage, while non-const have local linkage.
Also, why won't it work when I declare Bar as a pointer rather than an
array? I must admit to never having entirely understood the difference
between the two forms of declaration.

The gcc warning is enlightening:

/tmp/y.cc:13: error: 'bar' is not a valid template argument because
'bar' is a variable, not the address of a variable
Incidentally, why can't I typedef Bar as "extern const wchar_t Bar[]"?

I don't think linkage specifications can be part of a typedef. An
object has linkage, not a type.
 
R

Rennie deGraaf

Hello,

What are the rules concerning how to use a string as a template
parameter? I assume that the reason why it is necessary to use a named
object rather than a string literal is that the compiler needs to be
able to convert the value into an integer (or something similar), but I
haven't been able to make any sense about how it handles extern and
const.

For instance, consider the following code:

typedef const wchar_t Bar[];
//typedef wchar_t Bar[];
//typedef const wchar_t* Bar;
//typedef const wchar_t* const Bar;

extern Bar bar = L"bar";
//Bar bar = L"bar";

template<Bar>
struct Foo {};

Foo<bar> foo;

int main()
{
return 0;
}

When using the first definition of Bar (as a const array) and the first
definition of bar (as extern), it compiles*. When Bar is non-const (the
second definition), then it compiles whether or not bar is extern
(although I get a warning if it /is/ extern). But when I use const and
/not/ extern, it doesn't compile. Why?

Linkage?

extern consts have global linkage, while non-const have local linkage.

I've found statements on the web claiming that to use a named object as
a template parameter, it must have external linkage. Why is this
important? And is there a difference between the linkage of "wchar_t
bar[]" and "const wchar_t bar[]"?
The gcc warning is enlightening:

/tmp/y.cc:13: error: 'bar' is not a valid template argument because
'bar' is a variable, not the address of a variable

Why is that a problem when bar is defined as a pointer, but not when
defined as an array? If I use the pointer definition, then using "&bar"
as my template parameter would use the address of the pointer, not the
address of the string. (Not to mention that that would be a wchar_t**,
not a wchar_t*.) Is there some actual difference between "wchar_t* bar"
and "wchar_t bar[]" in this scenario, unlike most others?
 
J

Jonathan Lee

No.  Both 'bar' have external linkage.  "An array of blah" does not
really differ from "an array of constant blah".  It's only for the
compiler, so that individual elements of the latter array could not be
changed (or passed by a ref to non-const, and so on).

A question for clarification: a single const object has internal
linkage, but arrays of const values don't?

--Jonathan
 
V

Victor Bazarov

A question for clarification: a single const object has internal
linkage, but arrays of const values don't?

My mistake. Arrays of cv-qualified T have internal linkage unless
specified otherwise. Since arrays themselves cannot be cv-qualified
(they aren't really objects), they derive their cv-qualification from
the elements. 8.3.4 explains.

I blame my mistake on all the Quebec smoke in our atmosphere. :)

V
 
J

James Kanze

What are the rules concerning how to use a string as a template
parameter?

You can't use any class type as a template parameter.
I assume that the reason why it is necessary to use a named
object rather than a string literal is that the compiler needs
to be able to convert the value into an integer (or something
similar), but I haven't been able to make any sense about how
it handles extern and const.

String literals aren't strings:). You can't use a string
literal because it doesn't have linkage; the standard requires
template parameters to have external linkage.
For instance, consider the following code:
typedef const wchar_t Bar[];
//typedef wchar_t Bar[];
//typedef const wchar_t* Bar;
//typedef const wchar_t* const Bar;

Forget the typedef. In this case, they only cause confusion.
extern Bar bar = L"bar";
//Bar bar = L"bar";

extern wchar_t const bar[] = L"bar";

Or without the const.

The template argument should be "wchar_t const*".
template <Bar>
struct Foo {};
Foo<bar> foo;
int main()
{
return 0;
}
When using the first definition of Bar (as a const array) and
the first definition of bar (as extern), it compiles*. When
Bar is non-const (the second definition), then it compiles
whether or not bar is extern (although I get a warning if it
/is/ extern). But when I use const and /not/ extern, it
doesn't compile. Why?

Because const objects default to internal linkage, and template
arguments must have external linkage.
Also, why won't it work when I declare Bar as a pointer rather
than an array?

Not having access to the definition of Foo, I can't be sure; if
you want to instantiate Foo with a pointer, you have to declare
Foo to use a reference to a pointer, or provide a pointer which
is a compile time constant (to something with external linkage).
I must admit to never having entirely understood the difference
between the two forms of declaration.

Which two forms?
Incidentally, why can't I typedef Bar as "extern const wchar_t
Bar[]"?

Because "extern" is not part of a type. It's a storage class
specifiers. A typedef can only be used with type specifiers.
 
J

James Kanze

[...]
I've found statements on the web claiming that to use a named
object as a template parameter, it must have external linkage.
Why is this important?

Because the standard says so. Partially, at least, because it
wasn't too sure how to implement templates for objects without
external linkage.

The latter is especially true in the case of string literals,
since it is unspecified whether "bar" has the same address or
not in two different translation units.
And is there a difference between the linkage of "wchar_t
bar[]" and "const wchar_t bar[]"?

Yes. For histerical^h^h^h^h^h^horical reasons, an object
defined at namespace scope has internal linkage if it is const,
external otherwise.

Note that this only depends on the const-ness of the object
itself. If the object is a pointer, the const-ness of what is
pointed to has no effect. In other words, both
char const* p;
and
char* p;
have external linkage.

And finally, the rule only applies to "objects". A reference
defined at namespace scope has external linkage, even though it
is always "const". (As far as I know, there is no way of giving
a reference internal scope.)
Why is that a problem when bar is defined as a pointer, but not when
defined as an array?

Because a pointer is a variable, not a compile time constant.
If I use the pointer definition, then using "&bar" as my
template parameter would use the address of the pointer, not
the address of the string. (Not to mention that that would be
a wchar_t**, not a wchar_t*.) Is there some actual difference
between "wchar_t* bar" and "wchar_t bar[]" in this scenario,
unlike most others?

There are practically no scenarios where there isn't a
difference. Pointers and arrays are two completely different,
and largely unrelated things.
 
R

Rennie deGraaf

On May 31, 9:04 am, Rennie deGraaf <[email protected]>
If I use the pointer definition, then using "&bar" as my
template parameter would use the address of the pointer, not
the address of the string. (Not to mention that that would be
a wchar_t**, not a wchar_t*.) Is there some actual difference
between "wchar_t* bar" and "wchar_t bar[]" in this scenario,
unlike most others?

There are practically no scenarios where there isn't a
difference. Pointers and arrays are two completely different,
and largely unrelated things.

Semantically, they are different and largely unrelated things.
Syntactically, they are relatively few situations in which an array
differs from a pointer to an array.
 
J

James Kanze

On 06/01/2010 02:36 AM, James Kanze wrote:
If I use the pointer definition, then using "&bar" as my
template parameter would use the address of the pointer,
not the address of the string. (Not to mention that that
would be a wchar_t**, not a wchar_t*.) Is there some
actual difference between "wchar_t* bar" and "wchar_t
bar[]" in this scenario, unlike most others?
There are practically no scenarios where there isn't a
difference. Pointers and arrays are two completely
different, and largely unrelated things.
Semantically, they are different and largely unrelated things.
Syntactically, they are relatively few situations in which an
array differs from a pointer to an array.

Syntactically, the name of an array and the name of a pointer
are both symbols. And the syntax stops there. The rest is
semantics, and the two are fondamentally unrelated.
 
J

James Kanze

I think that's because almost all operations on an array
involve the implicit conversion of the array to a pointer.
Indeed, I'm trying to think what operations are possible on an
array (as opposed to the pointer that it converts to). The
only one I can think of is passing the array to a function.

The most obvious one is using an array as an operand of sizeof.
Also, of course, using it to initialize a reference. And the
most fundamental: what the definition defines.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top