C ++: шаблон кандидата игнорируется: недопустимый явно указанный аргумент для параметра шаблона

У меня есть такой заголовок функции:

template <
    bool src_alpha,
    int sbpp, int dbpp,
    typename T1, typename T2,
    Color (*getFunc)(T1 data, Uint8* addr),
    void (*putFunc)(T2 data, Uint8* addr, Color c)
>
static void OperateOnSurfaces(T1 data1, T2 data2, SDL_Surface * bmpDest, SDL_Surface * bmpSrc, SDL_Rect& rDest, SDL_Rect& rSrc)

Вот как я его использую:

OperateOnSurfaces<
    true,
    32, 32,
    SDL_PixelFormat*, SDL_PixelFormat*,
    GetPixel<true,32>, PutPixel<true,true,32> >(
    bmpSrc->format, bmpDest->format,
    bmpDest, bmpSrc, rDest, rSrc);

Это GetPixel и PutPixel:

template<bool alpha, int bpp>
static Color GetPixel(SDL_PixelFormat* format, Uint8* addr) { /* .. */ }

template<bool alpha, bool alphablend, int bpp>
static void PutPixel(SDL_PixelFormat* format, Uint8* addr, Color col) { /* .. */ }

И я получаю такую ​​ошибку:

note: candidate template ignored: invalid explicitly-specified argument for template parameter 'getFunc' [3]

Почему?


person Albert    schedule 25.12.2011    source источник
comment
Имена, начинающиеся с подчеркивания и заглавной буквы, зарезервированы, не используйте их. Кроме того, вам не хватает важной информации: Как вы вызываете эту функцию? Какие параметры?   -  person Xeo    schedule 25.12.2011
comment
Просто чтобы вы знали, имена, содержащие в любом месте двойное подчеркивание, так же зарезервированы, как и имена, начинающиеся с подчеркивания и заглавной буквы.   -  person Xeo    schedule 25.12.2011
comment
@Xeo: Спасибо за замечание. А как насчет трех? Какое хорошее соглашение для таких абстрактных функций? (Во всех таких случаях у меня также есть версии этих функций без подчеркивания.)   -  person Albert    schedule 25.12.2011
comment
Три символа подчеркивания содержат два символа подчеркивания, поэтому нельзя. Просто поместите их в namespace detail. Кроме того, это бесплатные функции или статические функции-члены? Кроме того, какой компилятор вы используете?   -  person Xeo    schedule 25.12.2011
comment
@Albert: Вы можете попробовать завершить имена переменных одним или несколькими символами подчеркивания. Например, руководство по стилю Google использует одиночное подчеркивание в конце имен переменных для переменных-членов класса.   -  person Ben Hocking    schedule 25.12.2011
comment
@Ben: двойные подчеркивания где угодно в имени зарезервированы.   -  person Xeo    schedule 25.12.2011
comment
Я думаю, что функции с внутренней связью (static) не могут использоваться в качестве аргументов шаблона, не являющегося типом.   -  person Kerrek SB    schedule 25.12.2011
comment
@Xeo: Вы правы. Ваше предложение пространства имен хорошее.   -  person Ben Hocking    schedule 25.12.2011


Ответы (1)


Я подозреваю, что все эти функции бесплатные. Когда вы объявляете бесплатную функцию static, она получает внутреннюю связь. Параметры шаблона, не являющиеся типами, в C ++ 03 должны иметь внешнюю связь . Просто удалите static перед функциями.

template <
    bool src_alpha,
    int sbpp, int dbpp,
    typename T1, typename T2,
    char (*getFunc)(T1 data, unsigned* addr),
    void (*putFunc)(T2 data, unsigned* addr, char c)
>
void OperateOnSurfaces(){}

template<bool alpha, int bpp>
char GetPixel(void* format, unsigned* addr);

template<bool alpha, bool alphablend, int bpp>
void PutPixel(void* format, unsigned* addr, char col);

int main(){
    OperateOnSurfaces<
        true,
        32, 32,
        void*, void*,
        GetPixel<true,32>, PutPixel<true,true,32> >();
}

Этот модифицированный пример отлично компилируется на Clang 3.1 и GCc 4.4.5 в режиме C ++ 98 и C ++ 11, без предупреждений. Если я оставлю static внутри, я получу аналогичную ошибку + примечание к тому, что у вас было с Clang, и GCC выдаст важную информацию (прокрутите вправо, не имеет внешней связи):

15:02:38 $ g++ t.cpp
t.cpp: In function ‘int main()’:
t.cpp:21: error: ‘GetPixel<true, 32>’ is not a valid template argument for type ‘char (*)(void*, unsigned int*)’ because function ‘char GetPixel(void*, unsigned int*) [with bool alpha = true, int bpp = 32]’ has not external linkage
t.cpp:21: error: ‘PutPixel<true, true, 32>’ is not a valid template argument for type ‘void (*)(void*, unsigned int*, char)’ because function ‘void PutPixel(void*, unsigned int*, char) [with bool alpha = true, bool alphablend = true, int bpp = 32]’ has not external linkage
t.cpp:21: error: no matching function for call to ‘OperateOnSurfaces()’

(C++03) §14.3.2 [temp.arg.nontype] p1

аргумент-шаблона для не-типа, не шаблона параметр-шаблона должен быть одним из:

  • [...]

  • адрес объекта или функции с внешней ссылкой [...]

  • [...]

Обратите внимание, что C ++ 11 изменил формулировку и теперь поддерживает функции с внутренней связью:

(C++11) §14.3.2 [temp.arg.nontype] p1

аргумент-шаблона для не-типа, не шаблона параметр-шаблона должен быть одним из:

  • [...]

  • постоянное выражение (5.19), которое обозначает адрес объекта со статической продолжительностью хранения и внешней или внутренней связью или функцию с внешней или внутренней связью [...]

  • [...]

Clang в настоящее время не подчиняется этому в режиме C ++ 11, он по-прежнему разрешает только функции с внешней связью.

person Xeo    schedule 25.12.2011
comment
Этот ответ верен только для c ++ 03. Текущий C ++ допускает также внутреннюю привязку. - person Johannes Schaub - litb; 25.12.2011
comment
Ах. Интересно, почему это так. Вся цель этой конструкции состоит в том, чтобы у компилятора была возможность встроить функцию. - person Albert; 25.12.2011
comment
@Albert Внешняя и внутренняя ссылки не имеют ничего общего с тем, будет ли компилятор встраивать материал. Тот факт, что вы используете функцию указатели, делает это очень маловероятным. Лучшее, что вы можете сделать, - это просто использовать функтор и передать его. При этом у компилятора есть вся информация, необходимая для встраивания вызова. - person Xeo; 25.12.2011
comment
@Xeo: Вы меня неправильно поняли. Я использую эту конструкцию исключительно для того, чтобы иметь возможность создать специальную версию OperateOnSurface с заданной известной статической встроенной функцией. С точки зрения компилятора, это не должно отличаться от функтора. И это делает код чище. - person Albert; 25.12.2011
comment
@Albert: Нет, этот код не чище, чем эквивалентная версия функтора. Кроме того, более вероятно, что компилятор встроит версию функтора. - person Xeo; 25.12.2011
comment
@Xeo: Думаю, это так. Может дело вкуса. Однако в любом случае у вас часто есть функции, которые вы хотите там использовать. См. это сравнение. - person Albert; 25.12.2011
comment
@Albert: Вы скомпилировали это без оптимизации? По крайней мере, кажется. Также обратите внимание, что ключевое слово inline - это фарс для создания встроенных функций. Компилятор может игнорировать это и делает это в большинстве случаев. То же самое касается его неиспользования, компилятор по-прежнему может встраивать такие функции. Это полезно только в том случае, если вы хотите определить в заголовке функцию, не являющуюся шаблоном, поскольку без нее вы бы получили несколько ошибок определения. Это почти единственное, что осталось. - person Xeo; 26.12.2011
comment
@Xeo: Без какой-либо опции, т.е. я думаю, что он равен -O0 для G ++. И просто попробуйте сами, вы увидите, что в этом случае он действительно встраивается именно тогда, когда есть inline. И да, я знаю, что компилятор не обязан строго этому следовать. Во всяком случае, это было сравнение указателей функций и функторов, и какое решение кажется более чистым и коротким. - person Albert; 26.12.2011