Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are external-linkage variables usable as constant expressions?

Tags:

c++

In a discussion on another question, I was given an example where apparently linkage of an identifier affected its usability in a constant expression:

extern char const a[] = "Alpha";
char constexpr b[] = "Beta";
char const g[] = "Gamma";

template <const char *> void foo() {}

auto main()
    -> int
{
    foo<a>();     // Compiles
    foo<b>();     // Compiles
    foo<g>();     // Doesn't compile
}

The error from the last (with GCC) is:

test.cc: In function 'int main()':
test.cc:12:13: error: the value of 'g' is not usable in a constant expression
         foo<g>();     // Doesn't compile
             ^
test.cc:3:16: note: 'g' was not declared 'constexpr'
     char const g[] = "Gamma";
                ^

I may have missed the significance of the example in the earlier discussion, because I believed that it could not have been just linkage which differentiated foo<a> from foo<g> - however, I have begun to doubt that position.

  1. Is it really the linkage, or is it some other attribute granted by extern, that allows foo<a>()?
  2. What is the rationale for allowing foo<a>() but not foo<g>()? In particular, if it is determined by linkage, why should internal linkage cause a variable not to be usable as a constant expression when the same variable declared extern would be usable?
  3. It was suggested that the question of the symbol being visible (or not) to the linker is relevant here. To me, it seems that the fact the foo<b> variant is still allowed even when static is added disproves this - or am I mistaken?
  4. (The difference between foo<b>() and foo<g>() is adequately covered by other questions, I think).
like image 316
davmac Avatar asked Oct 24 '16 21:10

davmac


1 Answers

GCC bug.

N3337 (which is C++11 + editorial fixes) [temp.arg.nontype]/2 has an example that's directly on point:

template<class T, const char* p> class X {
    /* ... */
};
X<int, "Studebaker"> x1; // error: string literal as template-argument

const char p[] = "Vivisectionist";
X<int,p> x2; // OK

In C++03 reference/pointer template arguments are limited to things with external linkage, but that restriction was removed in C++11.

The rules for reference/pointer template arguments are relaxed in C++17 to permit all constant expressions, so probably the reason why GCC accepts the example with -std=c++1z is that it goes through a different code path in that mode.

like image 110
T.C. Avatar answered Sep 18 '22 17:09

T.C.