Какой набор функций учитывается при разрешении перегруженных функций, присваивающих значениям параметров по умолчанию?
Рассмотрим приведенную ниже функцию bar
, параметр которой имеет значение по умолчанию, инициализированное вызовом перегруженного foo
:
#include <iostream>
int foo(int x)
{
std::cout << "foo(int)" << std::endl;
return 0;
}
template<typename T>
void bar(T a, int x = foo(T(0))) {}
double foo(double x)
{
std::cout << "foo(double)" << std::endl;
return 0;
}
int main()
{
bar<int>(1);
bar<double>(1);
return 0;
}
Я ожидаю, что эта программа выведет
foo(int)
foo(double)
соответствует foo
двум перегрузкам, которые видны при создании bar
.
Вместо этого, когда скомпилировано с g++-4.6
, вывод
$ g++-4.6 -std=c++0x test.cpp; ./a.out
foo(int)
foo(int)
Рассматривается ли множество перегрузок при реализации значения параметра по умолчанию, отличного от нормального разрешения перегрузки? Этот случай описан в стандарте ISO С++?
Ответы
Ответ 1
Вот что говорит об этом стандарт. Во-первых, в пункте 8.3.6 [dcl.fct.default], пункт 5:
... Имена в аргументе по умолчанию привязаны, и семантические ограничения проверяются в точке, где появляется аргумент по умолчанию. Поиск имени и проверка семантических ограничений для аргументов по умолчанию в шаблонах функций и в функциях-членах шаблонов классов выполняются, как описано в 14.7.1....
Далее в пункте 14.7.1 [temp.inst]:
Если шаблон функции f вызывается таким образом, что требуется использовать аргумент по умолчанию, зависимые имена просматриваются, проверяются ограничения семантики и создается экземпляр любого шаблона, используемого в аргументе по умолчанию, как если бы аргумент по умолчанию был инициализатором, используемым в специализации шаблона с той же областью, теми же параметрами шаблона и тем же доступом, что и шаблон функции f используется в этой точке. Этот анализ называется созданием аргументов по умолчанию. Конкретный аргумент по умолчанию затем используется как аргумент f.
Я не совсем уверен, что это точно говорит. В этом тексте я вижу обе интерпретации. В случае, если это имеет значение, я думаю, что EDG соглашается с gcc и clang.
Ответ 2
Эта программа предполагает, что набор рассмотренных функций следует нормальным правилам разрешения перегрузки:
#include <iostream>
struct type1 {};
struct type2 {};
int foo(type1 x)
{
std::cout << "foo(type1)" << std::endl;
return 0;
}
template<typename T>
void bar(int x = foo(T())) {}
int foo(type2 x)
{
std::cout << "foo(type2)" << std::endl;
return 0;
}
int main()
{
bar<type1>();
bar<type2>();
return 0;
}
При компиляции с g++-4.6
, эта программа выводит:
$ g++ test_2.cpp ; ./a.out
foo(type1)
foo(type2)
Другими словами, foo
разрешается через ADL при создании bar
.
Поведение для кода из OP, по-видимому, заключается в том, что ADL не применяется к примитивам, таким как int
и double
, поэтому единственными рассмотренными перегрузками являются те, которые были объявлены перед вызовом foo
. Нечетное поведение MSVC кажется нестандартным.
Ответ 3
Может быть, потому, что вы объявили и определили перегруженную функцию "foo (double)" после шаблона, так что она ничего не знает о ее существовании. → структурированное программирование.
Я бы предложил:
#include <iostream>
int foo(int x)
{
std::cout << "foo(int)" << std::endl;
return 0;
}
double foo(double x)
{
std::cout << "foo(double)" << std::endl;
return 0;
}
template<typename T>
void bar(T a, int x = foo(T(0))) {}
int main()
{
bar<int>(1);
bar<double>(1);
return 0;
}