14

C++ стандартный раздел 8.3.6.4 говорит, чтоПочему аргумент по умолчанию нельзя добавить позже в функциях шаблона?

Для функций, не шаблонных, аргументы по умолчанию могут быть добавлены в более поздние декларации функции в той же области. [...]

Но мой вопрос в том, почему он не допускается для функций шаблона? В чем причина не допускать добавления аргументов по умолчанию в более поздние объявления в той же области для функций шаблона?

Рассмотрите эту программу, которая компилируется в порядке. (Не шаблон функции) (см видеодемонстрацию here.)

#include <iostream> 

int f(int a,int b,int c=3); 
int f(int a,int b=9,int c); // default argument in middle, ok allowed 

int main() 
{ 
    f(3); 
    f(3,6); 
    f(3,6,9); 
    return 0; 
} 

int f(int a,int b,int c) 
{ 
    std::cout<<a<<' '<<b<<' '<<c<<'\n'; 
    return 0; 
} 

Но после проваливается в компиляции. (Шаблон функции) (см демо here.)

#include <iostream> 

template <typename T> 
void f(T a,int b,int c=3); 
template <typename T> 
void f(T a,int b=9,int c); // compiler error why??? 

int main() 
{ 
    f(3); 
    f(3,6); 
    f(3,6,9); 
    return 0; 
} 

template <typename T> 
void f(T a,int b,int c) 
{ 
    std::cout<<a<<' '<<b<<' '<<c<<'\n'; 
} 
+2

Отсутствует 'template '? – LogicStuff

+0

Не так ли * добавление * и более * переопределение * аргументов по умолчанию? – jaggedSpire

+2

[Здесь] (http://coliru.stacked-crooked.com/a/39117b9aa49c90a4) пример почти компиляции – jaggedSpire

ответ

9

Это историческое ограничение, которое было добавлено довольно рано в процессе стандартизации (оно было на C++ 98, но не в ARM).

Я не помню точной причины (и мой коллега, который почти наверняка был там, когда было принято решение). Тем не менее, у меня есть предположение ...

В то время все-единственные компиляторы создавали шаблоны, переигрывая токены через синтаксический анализ. Некоторые едва разбирались в шаблонах. Рассмотрим:

template<class T> struct S { 
    T f(T); // (1) 
}; 
template<class T> T S<T>::f(T p = 42) { return p; } // (2) 
S<int> s; // Causes the "real" parsing of (1), but not (2). 
int r = s.f(); // (3) 

При решении вызова (3), более старые компиляторы поэтому часто только имели доступ к реализованным декларации (1), а (2) по-прежнему не очень разбирается (только маркер-буфер). В результате такие компиляторы не знали о добавленном аргументе по умолчанию в (3).

Является ли подозрительная осторожность, поэтому комитет решает в целом запретить добавленные аргументы по умолчанию в шаблонах.

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

+0

V: в чем смысл (2) только токена буферизована? – Destructor

+4

Не могу поверить, на этот вопрос ответил эксперт по шаблонам и автор шаблонов на C++ полное руководство: David Vandevoorde – Destructor

+1

Re. «token buffered» ... Некоторые компиляторы (MSVC, составители на основе EDG, pre-3.4 GCC и большинство старых компиляторов, таких как Cfront) обрабатывают шаблоны, «буферизируя» маркеры, из которых они состоят. Затем во время создания экземпляра эти токены «переигрываются» через анализатор, за исключением того, что маркеры параметров шаблона заменяются соответствующим аргументом шаблона. Таким образом, шаблон может находиться в состоянии «только токены, буферизованные, а не разобранные». –

-6

Потому что это принципиально невозможно.

Для достижения вашей цели компилятор должен реализовать функцию, которая, учитывая две функции шаблона, возвращает, являются ли они одной и той же функцией. Проблема в том, что эта функция не реализуема.

Для обычных функций без шаблонов это ужасная идея, но по-прежнему возможна, потому что вам просто нужно сопоставить типы аргументов и выполненную работу.

К сожалению, для функций шаблона это становится ... более сложным. Рассмотрим:

template<typename T> void f(T t); 
template<typename U> std::enable_if_t<std::is_same<U, int>::value> f(U u = U()); 

Вы можете видеть, что они, вероятно, объявить ту же функцию, еслиT является int. В противном случае они этого не делают. Есть более проблематичные взаимодействия с аргументами шаблона по умолчанию и схожими вещами, но длинный рассказ короче, это было бы неразрешимым для шаблонов.

+8

Компилятор * уже * должен выполнять сопоставление соответствия; вам разрешено объявлять один и тот же шаблон функции несколько раз. –

+0

@ T.C .: Это фактически не требует соответствия декларации. – Puppy

+0

* «Есть более проблематичные взаимодействия с аргументами шаблона по умолчанию и схожими вещами» * AFAIK, вы можете добавить аргументы шаблона по умолчанию для более позднего повторного объявления шаблона функции. Это то, о чем вы намекаете? Если да, то почему этот случай отличается? – dyp

Смежные вопросы