Последующий вопрос к [Приводит ли указатель к шаблону экземпляр этого шаблона?] .
Вопрос такой же, как в названии, с остальной частью вопросаограничения и примеры использования шаблона класса, а также мои попытки достичь цели.
Важное ограничение: пользователь создает экземпляр шаблона путем создания подкласса моего шаблона класса (а не путем явной его реализации, как в моих попытках).ниже).Поэтому для меня важно, чтобы, по возможности, пользователю не нужно было выполнять какую-либо дополнительную работу.Просто создание подкласса, и оно должно работать (подкласс фактически регистрирует себя в словаре уже без того, чтобы пользователь делал что-либо кроме подкласса дополнительного шаблона класса с CRTP, и подкласс никогда не используется непосредственно пользователем, который его создал).Я готов принять ответы, когда пользователь должен выполнить дополнительную работу (например, получить дополнительную базу), если другого пути нет.
Фрагмент кода, объясняющий, как шаблон классабудет использоваться:
// the class template in question
template<class Resource>
struct loader
{
typedef Resource res_type;
virtual res_type load(std::string const& path) const = 0;
virtual void unload(res_type const& res) const = 0;
};
template<class Resource, class Derived>
struct implement_loader
: loader<Resource>
, auto_register_in_dict<Derived>
{
};
template<class Resource>
Resource load(std::string const& path){
// error should be triggered here
check_loader_instantiated_with<Resource>();
// search through resource cache
// ...
// if not yet loaded, load from disk
// loader_dict is a mapping from strings (the file extension) to loader pointers
auto loader_dict = get_all_loaders_for<Resource>();
auto loader_it = loader_dict.find(get_extension(path))
if(loader_it != loader_dict.end())
return (*loader_it)->load(path);
// if not found, throw some exception saying that
// no loader for that specific file extension was found
}
// the above code comes from my library, the code below is from the user
struct some_loader
: the_lib::implement_loader<my_fancy_struct, some_loader>
{
// to be called during registration of the loader
static std::string extension(){ return "mfs"; }
// override the functions and load the resource
};
А теперь в табличной форме:
- Пользователь звонит
the_lib::load<my_fancy_struct>
с путем к ресурсу - Внутри
the_lib::load<my_fancy_struct>
,если ресурс, указанный по пути, еще не кэширован, я загружаю его с диска - Конкретный
loader
, который будет использоваться в этом случае, создается во время запуска и сохраняется в словаре - Если словарь пуст, пользователь либо
- не создал загрузчикдля этого конкретного расширения или
- не создал загрузчик для этого конкретного ресурса
- Я хочу, чтобы только в первом случае я вызывал исключение времени выполнения
- Второй случай должен быть обнаружен при компиляции /время компоновки, так как оно включает шаблоны
Обоснование: Я полностью поддерживаю ранние ошибки и, если возможно, хочу обнаружить как можно больше ошибок до времени выполнения, т.е.время компиляции и компоновки.Поскольку проверка наличия загрузчика для этого ресурса будет включать только шаблоны, я надеюсь, что это возможно сделать.
Цель в моих попытках: вызвать ошибку компоновщика при вызове check_error<char>
.
// invoke with -std=c++0x on Clang and GCC, MSVC10+ already does this implicitly
#include <type_traits>
// the second parameter is for overload resolution in the first test
// literal '0' converts to as well to 'void*' as to 'foo<T>*'
// but it converts better to 'int' than to 'long'
template<class T>
void check_error(void*, long = 0);
template<class T>
struct foo{
template<class U>
friend typename std::enable_if<
std::is_same<T,U>::value
>::type check_error(foo<T>*, int = 0){}
};
template struct foo<int>;
void test();
int main(){ test(); }
Учитывая приведенный выше код, следующее определение test
действительно достигает цели для MSVC, GCC 4.4.5 и GCC 4.5.1 :
void test(){
check_error<int>(0, 0); // no linker error
check_error<char>(0, 0); // linker error for this call
}
Однако этого делать не следует, так как передача нулевого указателя не вызывает ADL.Зачем нужен ADL?Поскольку стандарт гласит:
§7.3.1.2 [namespace.memdef] p3
[...] Если объявление friend
в нелокальном классе сначала объявляет класс или функцию, класс или функцию другаявляется членом внутреннего вложенного пространства имен. Имя друга не найдено ни в неквалифицированном поиске, ни в квалифицированном поиске, пока в этой области пространства имен не будет предоставлено соответствующее объявление (ни до, ни после определения класса, предоставляющего дружбу).[...]
Запуск ADL через бросок, как в следующем определении test
, достигает цели на Clang 3.1 и GCC 4.4.5, но GCC 4.5.1уже отлично связывает , как и MSVC10:
void test(){
check_error<int>((foo<int>*)0);
check_error<char>((foo<char>*)0);
}
К сожалению, GCC 4.5.1 и MSVC10 ведут себя здесь корректно, как обсуждалось в связанном вопросе и, в частности, этот ответ .