C ++ ссылки и шаблоны специализаций - PullRequest
12 голосов
/ 25 августа 2011

Я изучаю поведение компоновщика C ++ относительно специализаций шаблонов.Я использую Microsoft Visual C ++ 2010 для этих экспериментов.Я не знаю, то же самое ли поведение с другими инструментальными цепочками (например, gcc).

Вот первый фрагмент кода:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> int foo<double>() { return 2; }

int bar();

int main()
{
    const int x = bar();
    const int y = foo<double>();  // doesn't link
}

Ожидается, что этот код не связывается, потому что foo<double>() имеет несколько определений, поскольку он создается один раз в bar.cpp и один раз в main.cpp (через специализацию).Затем можно было бы ожидать, , если эта программа будет связывать , что bar() и main() будут использовать различные экземпляры foo(), так что в конце мы получим x == 1 и y == 2.

Давайте исправим ошибку ссылки, объявив специализацию foo<double>() как static:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> static int foo<double>() { return 2; }  // note: static

int bar();

int main()
{
    const int x = bar();          // x == 1
    const int y = foo<double>();  // y == 2
}

Теперь у нас есть x == 1 и y == 2, как мы и ожидали,(Примечание: здесь мы должны использовать ключевое слово static: анонимное пространство имен не подойдет, поскольку мы не можем специализировать функцию шаблона в другом пространстве имен, чем ее объявление.)

Теперь использование ключевого слова static довольно неинтуитивно.Как правило, специализация foo<double>() будет находиться где-то в заголовочном файле и, таким образом, будет помечена как встроенная, как в следующем фрагменте:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; }  // note: inline

int bar();

int main()
{
    const int x = bar();          // x == 2
    const int y = foo<double>();  // y == 2
}

Этот код теперь правильно связывается, и при его запуске мы получаемx == 2 и y == 2. Это немного удивляет: почему существует единственное определение foo<double>()?Что означает inline в этом коде?

Последний фрагмент:

// bar.cpp

template <typename T> int foo() { return 1; }
int bar() { return foo<double>(); }

// main.cpp

template <typename T> int foo() { return 1; }
template <> inline int foo<double>() { return 2; }  // note: inline

int bar();

int main()
{
    const int x = bar();             // x == 1
    // const int y = foo<double>();  // note: commented out
}

Этот случай на самом деле не удивителен: специализация foo<double>() больше не являетсясоздается в main.cpp (хотя декларация все еще там), поэтому единственная оставшаяся инстанция - это в bar.cpp .

1 Ответ

14 голосов
/ 25 августа 2011

Вы фактически нарушаете правило C ++ здесь (выделено мое):

14.7.3 [temp.expl.spec] :

6 / Если шаблон, шаблон элемента или член шаблона класса явно специализированы, то эта специализация должна быть объявлена ​​перед первым использованием этой специализации, которая вызовет неявную реализацию в каждой единице переводав котором такое использование происходит; Диагностика не требуется .Если программа не предоставляет определения для явной специализации, и либо специализация используется таким образом, чтобы вызвать неявное создание экземпляра, либо член является функцией виртуального члена, программа не сформирована, неттребуется диагностика .Неявная реализация никогда не генерируется для явной специализации, которая объявлена, но не определена.[ Пример :

class String { };

template<class T> class Array { /* ... */ };
template<class T> void sort(Array<T>& v) { /* ... */ }

void f(Array<String>& v) {
  sort(v); // use primary template
  // sort(Array<T>&), T is String
}

template<> void sort<String>(Array<String>& v); // error: specialization
                                                // after use of primary template

template<> void sort<>(Array<char*>& v); // OK: sort<char*> not yet used

template<class T> struct A {
  enum E : T;
  enum class S : T;
};

template<> enum A<int>::E : int { eint }; // OK
template<> enum class A<int>::S : int { sint }; // OK
template<class T> enum A<T>::E : T { eT };
template<class T> enum class A<T>::S : T { sT };
template<> enum A<char>::E : int { echar }; // ill-formed,
                                            // A<char>::E was instantiated
                                            // when A<char> was instantiated
template<> enum class A<char>::S : int { schar }; // OK

- конец примера ]

...