Подтвердить что ты не робот

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

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

4b9b3361

Ответ 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; 
}