Votre compilateur en fin de compte fonctionne sur des choses que l'on appelle les unités de traduction, officieusement appelé les fichiers source. Au sein de ces unités de traduction, vous identifiez les différentes entités: des objets, des fonctions, etc. Les linkers rôle est de relier ces unités, et une partie de ce processus est la fusion des identités.
Les identificateurs de liaison†: une liaison interne signifie que l'entité nommée dans cette unité de traduction est visible uniquement pour l'unité de traduction, tandis que la liaison externe signifie que l'entité est visible par les autres unités.
Lorsqu'une entité est marquée static
,, elle est donnée interne de liaison. Donc, compte tenu de ces deux unités de traduction:
// a.cpp
static void foo() { /* in a */ }
// b.cpp
static void foo() { /* in a */ }
Chacun de ceux - foo
s'fait référence à une entité (une fonction dans ce cas) qui n'est visible que pour leur respective des unités de traduction; c'est, chaque unité de traduction a sa propre foo
.
Le hic, c'est, ensuite: les littéraux de chaîne sont du même type que static const char[..]
. C'est:
// str.cpp
#include <iostream>
// this code:
void bar()
{
std::cout << "abc" << std::endl;
}
// is conceptually equivalent to:
static const char[4] __literal0 = {'a', 'b', 'c', 0};
void bar()
{
std::cout << __literal0 << std::endl;
}
Et comme vous pouvez le voir, la traduction littérale de la valeur interne de l'unité de traduction. Donc, si vous utilisez "abc"
dans plusieurs unités de traduction, par exemple, tous finissent par être différentes entités.‡
Dans l'ensemble, cela signifie que ce est conceptuellement pas de sens:
template <const char* String>
struct baz {};
typedef baz<"abc"> incoherent;
Parce qu' "abc"
est différent pour chaque unité de traduction. Chaque unité de traduction sera différente de la classe parce que chaque "abc"
est une entité différente, même si elles ont fourni le "même" argument.
Sur le niveau de langue, c'est imposé en disant que le modèle non-type de paramètres peuvent être des pointeurs vers des entités de liaison externe; c'est, des choses qui ne se réfèrent à la même entité à travers les unités de traduction.
Donc c'est très bien:
// good.hpp
extern const char* my_string;
// good.cpp
const char* my_string = "any string";
// anything.cpp
typedef baz<my_string> coherent; // okay; all instantiations use the same entity
†Pas tous les identificateurs de liaison; certains n'en ont aucun, tels que les paramètres de la fonction.
‡ Un compilateur optimisant va stocker identiques littéraux à la même adresse, pour économiser de l'espace; mais c'est une qualité de mise en œuvre détail, pas une garantie.