Undefined метод трюков шаблонов?
Один из моих коллег рассказал мне о небольшом дизайне, которое он использовал со своей командой, которая послала мой мозг. Это своего рода класс признаков, который они могут специализировать в чрезвычайно развязанной форме.
Мне было трудно понять, как это может работать, и я все еще не уверен в своей идее, поэтому я подумал, что попрошу о помощи здесь.
Мы говорим 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;
};
Это символ undefined, если единица перевода не определила специализацию Interface
для Special
.
Теперь я бы подумал, что для этого потребуется ключевое слово export
, которое, насколько мне известно, никогда не было реализовано в g++ (и реализовано только один раз в компиляторе на С++, а его авторы советуют кому-то не делать этого, учитывая время и усилия, которые им потребовались).
Я подозреваю, что это как-то связано с компоновщиком, разрешающим методы шаблонов...
- Вы когда-либо встречали что-либо подобное раньше?
- Соответствует ли он стандарту или вы считаете, что это удачное совпадение, которое оно работает?
Я должен признать, что я довольно озадачен конструкцией...
Ответы
Ответ 1
Как и подозреваемый @Steward, он недействителен. Формально это эффективно вызывает поведение undefined, потому что Стандарт устанавливает, что для нарушения не требуется никакой диагностики, а это означает, что реализация может молча делать все, что захочет. В 14.7.3/6
Если шаблон, шаблон-член или член шаблона класса явно специализированы, то эта специализация должна быть объявлена до первого использования этой специализации, которая вызовет неявное создание экземпляра, в каждой единицы перевода, в которой такие используется использование; диагностика не требуется.
На практике, по крайней мере, на GCC, он неявно создает экземпляр первичного шаблона Interface<T>
, поскольку специализация не была объявлена и не видна в main
, а затем вызывает Interface<T>::foo
. Если его определение видимо, оно инициализирует основное определение функции-члена (поэтому, когда оно определено, оно не будет работать).
Инициированные символы имен функций имеют слабую связь, потому что они могут присутствовать несколько раз в разных объектных файлах и должны быть объединены в один символ в финальной программе. Напротив, члены явных специализаций, которые не являются шаблонами, больше имеют прочную связь, поэтому они будут доминировать над слабыми символами привязки и превращать вызов в специализацию. Все это детализация реализации, и в Стандарте нет такого понятия слабой/сильной связи. Вы должны объявить специализацию до создания объекта special
:
template <>
struct Interface<Special>;
Стандарт оставляет его открытым (подчеркивайте мной)
Размещение явных деклараций специализации для шаблонов функций, шаблонов классов, функций-членов шаблонов классов, элементов статических данных шаблонов классов, классов-членов шаблонов классов, шаблонов классов-членов шаблонов классов, шаблонов-членов шаблонов классов, члена функции шаблонов-членов шаблонов классов, функции-члены шаблонов-членов классов, отличных от шаблонов, шаблоны-члены-члены классов-членов шаблонов классов и т.д., а также размещение объявлений частичной специализации шаблонов классов, шаблонов классов-членов не-шаблона классы, шаблоны классов членов шаблонов классов и т.д. могут повлиять на то, хорошо ли сформирована программа в соответствии с относительным позиционированием явных объявлений специализации и их точками инстанцирования в блоке трансляции, как указано выше и ниже. При написании специализации будьте осторожны с ее местоположением; или сделать его компиляцией, станет таким испытанием, чтобы разжечь его самосожжение.
Ответ 2
Это довольно аккуратно. Я не уверен, что гарантировано работать везде. Похоже, что у них есть намеренно undefined метод шаблона, а затем определение специализации, спрятанной в своей собственной единицы перевода. Они зависят от компилятора, использующего одно и то же имя, как для исходного метода шаблона класса, так и для специализации, который, по-моему, является нестандартным. Затем компоновщик будет искать метод шаблона класса, но вместо этого найдет специализацию.
Есть несколько рисков с этим, хотя. Никто, даже не компоновщик, не возьмет несколько реализаций метода, например. Методы шаблона будут отмечены selectany, потому что шаблон подразумевает inline, поэтому, если компоновщик видит несколько экземпляров, вместо того, чтобы выдавать ошибку, он выбирает тот, который наиболее удобен.
Тем не менее, хороший трюк, хотя, к сожалению, похоже, что это удачное совпадение, что оно работает.