Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

constexpr string literals in anonymous namespace?

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)

like image 273
Steve Lorimer Avatar asked Feb 19 '13 06:02

Steve Lorimer


2 Answers

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.

like image 60
Steve Lorimer Avatar answered Oct 12 '22 21:10

Steve Lorimer


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.

like image 23
ecatmur Avatar answered Oct 12 '22 21:10

ecatmur