Неопределенный шаблонный прием методов?

Мой коллега сказал мне о маленьком кусочке дизайна, что использовал со своей командой, которая отправила мое кипение ума. Именно своего рода класс черт они могут специализироваться чрезвычайно отделенным способом.

Мне было нелегко понимать, как это могло возможно работать, и я все еще не уверен в идее, которую я имею, таким образом, я думал, что обращусь за помощью здесь.

Мы говорим g ++ здесь, конкретно версии 3.4.2 и 4.3.2 (это, кажется, работает с обоими).

Идея довольно проста:

1-Определяют интерфейс

// interface.h
template <class T>
struct Interface
{
  void foo(); // the method is not implemented, it could not work if it was
};

//
// I do not think it is necessary
// but they prefer free-standing methods with templates
// because of the automatic argument deduction
//
template <class T>
void foo(Interface<T>& interface) { interface.foo(); }

2-Определяют класс, и в исходном файле специализируют интерфейс для этого класса (определяющий его методы)

// special.h

class Special {};


// special.cpp

#include "interface.h"
#include "special.h"

// 
// Note that this specialization is not visible outside of this translation unit
//
template <>
struct Interface<Special>
{
  void foo() { std::cout << "Special" << std::endl; }
};

3-Для использования это просто также:

// main.cpp

#include "interface.h"

class Special; // yes, it only costs a forward declaration
               // which helps much in term of dependencies

int main(int argc, char* argv[])
{
  Interface<Special> special;
  foo(special);
  return 0;
};

Это - неопределенный символ, если никакая единица перевода не определила специализацию Interface для Special.

Теперь, я думал бы, что это потребует export ключевое слово, чтобы к моему знанию никогда не реализовывался в g ++ (и только реализовывался однажды в компиляторе C++, с его авторами, советующими любому не к, давался время и усилие, это взяло их).

Я подозреваю, что это имеет что-то, чтобы сделать с компоновщиком, разрешающим шаблонные методы...

  • Вы имеете когда-нибудь встреченными что-нибудь как это прежде?
  • Это соответствует стандарту, или Вы думаете, что это - удачное совпадение, это работает?

Я должен признать, что вполне озадачен конструкцией...

13
задан Georg Fritzsche 9 May 2010 в 12:27
поделиться

2 ответа

Как и подозревал @Steward, это не корректно. Формально это эффективно вызывает неопределенное поведение, потому что Стандарт предписывает, что для нарушения не требуется диагностика, что означает, что реализация может молча делать все, что захочет. В 14.7.3/6

Если шаблон, шаблон-член или член шаблона класса явно специализирован, то эта специализация должна быть объявлена до первого использования этой специализации, которое может привести к неявному инстанцированию, в каждой единице перевода, в которой происходит такое использование; диагностика не требуется.

На практике, по крайней мере в GCC, это неявное инстанцирование первичного шаблона Interface, поскольку специализация не была объявлена и не видна в main, а затем вызов Interface::foo. Если ее определение видно, то она инстанцирует первичное определение функции-члена (вот почему, когда она определена, это не сработает).

Инстанцированные символы имен функций имеют слабую связь, потому что они могут присутствовать несколько раз в разных объектных файлах, и их приходится объединять в один символ в конечной программе. Напротив, члены явных специализаций, которые уже не являются шаблонами, имеют сильную связь, поэтому они будут доминировать над символами слабой связи и заставят вызов заканчиваться в специализации. Все это детали реализации, и в Стандарте нет такого понятия слабой/сильной связи. Вы должны объявить специализацию до создания специального объекта:

template <>
struct Interface<Special>;

В стандарте это показано наглядно (выделено мной)

Размещение явных объявлений специализации для шаблонов функций, шаблонов классов, функций-членов шаблонов классов, статических членов данных шаблонов классов, классов-членов шаблонов классов, шаблонов классов-членов шаблонов классов, шаблонов функций-членов шаблонов классов, функций-членов шаблонов классов, функций-членов шаблонов классов, функций-членов шаблонов нешаблонов классов, шаблонов функций-членов классов-членов шаблонов классов и т.д., и размещение деклараций частичной специализации шаблонов классов, шаблонов классов-членов нешаблонов классов, шаблонов классов-членов шаблонов классов и т.д. может повлиять на то, является ли программа хорошо сформированной в соответствии с относительным расположением деклараций явной специализации и их точек инстанцирования в единице трансляции, как указано выше и ниже. При написании специализации будьте внимательны к ее расположению; иначе ее компиляция станет таким испытанием, которое приведет к ее самосожжению.

10
ответ дан 2 December 2019 в 00:17
поделиться

Неплохо. Я не уверен, что он гарантированно будет работать везде. Похоже, что они делают намеренно неопределенный метод шаблона, а затем определяют специализацию, спрятанную в ее собственной единице перевода. Они зависят от компилятора, использующего одноименное искажение как для исходного метода шаблона класса, так и для специализации, что, на мой взгляд, является нестандартным. Затем компоновщик будет искать метод шаблона класса, но вместо этого найдет специализацию.

Однако здесь есть несколько рисков. Например, никто, даже компоновщик, не возьмется за несколько реализаций метода. Методы шаблона будут помечены как selectany, потому что шаблон подразумевает встроенный, поэтому, если компоновщик видит несколько экземпляров, вместо того, чтобы выдавать ошибку, он выберет наиболее удобный.

Тем не менее, это хороший трюк, хотя, к сожалению, кажется счастливым совпадением, что он работает.

5
ответ дан 2 December 2019 в 00:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: