Какой набор функций учитывается при разрешении перегруженных функций, присваивающих значениям параметров по умолчанию?

Рассмотрим приведенную ниже функцию 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; 
}