Я играю с вариативными шаблонами C ++ 11, и вот мой код:

#include "iostream"
void func(){
    std::cout << std::endl;
}

template< typename ...Params> void func(int x, Params... params){
    std::cout << " int " << x;
    func(params...);
}
template< typename ...Params> void func(float x, Params... params){
    std::cout << " float " << x;
    func(params...);
}
template< typename ...Params> void func(const char* x, Params... params){
    std::cout << " const char* " << x;
    func(params...);
}

int main(int argc, char* argv[])
{
    func(3.14f, 5, "Test");
    getchar();
    return 0;
}

Приведенный выше код можно скомпилировать и запустить в Visual Studio 2013 без каких-либо проблем, но если я скомпилирую его с помощью gcc 4.6.3 (у меня нет среды gcc и я использую онлайн-сервис, например repl.it), этот код выдаст такую ​​ошибку:

main.cpp: In instantiation of 'void func(int, Params ...) [with Params = {const char*}]':
main.cpp:15:6:   required from 'void func(float, Params ...) [with Params = {int, const char*}]'
main.cpp:24:23:   required from here
main.cpp:11:6: error: no matching function for call to 'func(const char*&)'
  func(params...);
  ~~~~^~~~~~~~~~~
main.cpp:5:6: note: candidate: void func()
 void func(){
      ^~~~
main.cpp:5:6: note:   candidate expects 0 arguments, 1 provided
main.cpp:9:36: note: candidate: template<class ... Params> void func(int, Params ...)
 template< typename ...Params> void func(int x, Params... params){
                                    ^~~~
main.cpp:9:36: note:   template argument deduction/substitution failed:
main.cpp:11:6: note:   cannot convert 'params#0' (type 'const char*') to type 'int'
  func(params...);
  ~~~~^~~~~~~~~~~

exit status 1

Чье поведение правильно? В этом коде есть проблема, которую я не осознаю, или это ошибка gcc?

Кстати: если я заменю func(3.14f,5,"test") на func(3.14f,5), то gcc также сможет скомпилировать этот код.

1
ZumiKua 3 Авг 2017 в 07:51

1 ответ

Лучший ответ

Поведение gcc правильное.

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

В вашем случае первый шаблон создает экземпляр, эквивалентный

void func(int x, const char *params0){
    std::cout << " int " << x;
    func(params0);
}

И последний вызов недействителен на момент определения шаблона . В самом деле, шаблон, который может обрабатывать вызов, не объявляется позже в коде.

VC ++ известен своей несовместимой обработкой шаблонов: он проверяет правильность создания экземпляра в точке создания экземпляра , а не в точке определения исходного шаблона. Этот код прекрасно это иллюстрирует.

Чтобы привести код в соответствие, сначала объявите шаблоны, а затем определите их.

2
n. 1.8e9-where's-my-share m. 3 Авг 2017 в 09:18
Обратите внимание, что этот код не компилируется в MSVC 19.11.25506 с флагом /permissive-.
 – 
tambre
3 Авг 2017 в 09:22