9

I have the following example code which uses a string literal as a template parameter, such that the base class template can access the string.

The code compiles, but I get a warning which I do not fully understand:

warning: ‘ns::bar::type’ has a base ‘ns::base<((const char*)(& ns::bar::name))>’ whose type uses the anonymous namespace [enabled by default]

Working example code below:

// "test.h"
#pragma once

namespace ns 
{
    template <char const* str>
    struct base
    {
        const char *name() const { return str; }
    };

    namespace bar 
    {
        static constexpr char name[] = "bar";
        struct type : base<name> {};                // <-- this line here
    }
}

// main.cpp
#include <iostream>
#include "test.h"

int main()
{
    ns::bar::type f;
    std::cout << f.name() << std::endl;
    return 0;
}

So my questions are:

  1. What does this warning mean?
  2. Is it safe to pass a string literal as a template parameter in the way that I am doing here?

(Note this is with gcc 4.7.2)

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • #2 is easy to answer: Yes it's safe, as long as you always use `ns::bar::name` as the template parameter; another, separate string with the contents "bar" may not be at the same location in memory (and certainly won't be with optimizations off, e.g. during a debug build), which would lead to a separate `base<>` instantiation (albeit one which behaves the same way). – Cameron Feb 19 '13 at 06:34
  • So I could potentially use a `#pragma` to disable this warning around this particular instantiation and still feel warm and happy? (if one such pragma actually exists - can't seem to find it with a cursory google) – Steve Lorimer Feb 19 '13 at 06:36
  • No idea ;-) The warning is new to me too. I only addressed the second half of your question. What compiler are you using? (Ah never mind, I see it's gcc 4.7.2). I can't reproduce the warning with gcc 4.7.2. And I don't see the *anonymous* namespace you mentioned? – Cameron Feb 19 '13 at 06:40
  • Hmmm - considering that a type with internal linkage defined in a header will be a different type in every file that includes the header, would doing this actually be in error? Perhaps #2 is not safe after all? – Steve Lorimer Feb 19 '13 at 06:40
  • @Cameron - are you putting splitting the code out into 2 separate files? The first half has to be in a header. If you compile it all in 1 file then you don't get the warning - I'm guessing because there is now no internal-linkage issue? – Steve Lorimer Feb 19 '13 at 06:42
  • @lori: Oops, there's my mistake! I put it all in one file. Yes, you are right -- #2 shouldn't be safe unless `name` exists (in terms of allocated memory) only in one translation unit. – Cameron Feb 19 '13 at 06:44

2 Answers2

7

The issue is do with the fact that static constexpr char name[] = "bar"; has internal linkage.

Any type with internal linkage defined in a header will be a different type in every file that includes the header.

This is seldom the intention - hence the warning.

The reason there is no warning for when this is done in a source file is because that type can't ever be referred to by more than one file - hence it will always be that one type.

Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
4

The warning is because name will have a different address in each source file that test.h is included into. Because it has internal linkage (static) each translation unit will get its own copy; they will not be unified by the linker. This means that your code is equivalent to:

template<int> struct base { ... };
static constexpr int val = some_value_different_in_every_source_file;
struct type: base<val> {};

Your code is legal as presented, but if you include test.h in another source file then it will violate the one-definition rule:

3.2 One definition rule [basic.def.odr]

[...]
6 - There can be more than one definition of a class type [...] provided [...]: [...]

  • in each definition of D, corresponding names [...] can refer to a const object with internal or no linkage [only] if [...] the value (but not the address) of the object is used [...]

You're using the address of an object with internal linkage in the definition of a class type, so using it in more than one translation unit is undefined behaviour.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366