Как уже упоминалось другими авторами, вы разыменовываете указатель на TaskScheduler
без определения типа, что приведет к ошибке, как и в любом определении.
Что вас, вероятно, смущает, так это то, что ваш код, вероятно, работает на некоторых компиляторах, даже современных (я знаю, что MSVC неверен в этом отношении, но я не знаю, примет ли он вышеуказанный код *). Эти компиляторы неправильно реализуют так называемый поиск по двухфазному имени .
Двухфазная петля имен - более предсказуемый метод поиска имен, используемый в шаблонах, чем более простая форма, используемая некоторыми компиляторами. В более простой форме определение шаблона анализируется и сохраняется для использования только в том случае, если оно создано, а поиск имен выполняется для всех имен в шаблоне с момента создания экземпляра шаблона. При двухфазном поиске имен имена, используемые в шаблоне, сортируются в зависимые имена и независимые имена . Независимые имена - это имена, которые компилятор может разрешить немедленно - любое имя, которое не зависит от параметра шаблона, прямо или косвенно. Эти имена обрабатываются сразу же после определения шаблона. Зависимые имена, с другой стороны, не могут быть решены немедленно; они сохраняются и затем, когда выполняется создание экземпляра, ищутся в контексте шаблона, но также и в контексте, в котором был создан экземпляр шаблона для аргумент-зависимый поиск только .
Вот пример:
void foo (int);
template <typename T> void bar(T t) {
foo(1.0);
foo(t);
}
void foo (double);
struct qux {};
void foo (qux);
void baz () {
bar (1.0);
qux q;
bar (q);
}
N.B. Я знаю, что получил метасинтаксические имена в неправильном порядке. Я прошу прощения, но я добавил qux
в последний раз и не мог потрудиться переписать свой комментарий.
Каждый раз bar
шаблонов вызывается foo
дважды. Первый вызов не зависит, поэтому компилятор разрешает его, как только видит. В результате он вызывает foo (int)
, применяя преобразование, хотя позднее он найдет лучшее определение. Это ничем не отличается от любого другого вызова функции в C ++. Сложный бит приходит со вторым вызовом, который зависит. Первый звонок в baz
вызывает bar<double>
, последний звонит bar<qux>
. Созданный экземпляр bar
пытается вызвать foo
с объектом типа T
. В сценарии double
, так как примитивы никогда не используют зависимый от аргумента поиск, результат снова ищется только из bar
, и foo(int)
найдено. Однако при вызове с qux
зависимый от аргумента поиск применяется как в определении , так и в контексте создания экземпляра **, поэтому вызывается foo(qux)
.
Это может быть немного глупо, но это имеет тенденцию делать правильные вещи. Кроме того, я надеюсь, что вы действительно поняли это; это может быть довольно запутанным. Вам нужно прочитать эту ссылку в Википедии, чтобы полностью понять.
* MSVC может реализовывать меньшую форму двухфазного поиска имен, где она правильно разрешает независимые имена, но учитывает определения после шаблона для зависимых имен. Я забыл, делает ли он это или полностью пропускает двухфазный поиск, и у меня нет копии программы для проверки.
** Почти в каждом случае контекст создания включает в себя каждое объявление, которое делает контекст определения. Однако есть ключевое слово export
, которое может не соответствовать действительности. Это ключевое слово реализовано только в одном интерфейсе компилятора - интересно, почему никто другой не реализовал его? [/ Сарказм]