Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What set of functions is considered when resolving overloaded functions assigning to default parameter values?

Consider the function bar below, whose parameter has a default value initialized from an invocation of an overloaded 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;
}

I expect this program to output

foo(int)
foo(double)

corresponding to foo's two overloads which are visible at bar's instantiation.

Instead, when compiled with g++-4.6, the output is

$ g++-4.6 -std=c++0x test.cpp; ./a.out 
foo(int)
foo(int)

Is the set of overloads considered when implementing a default parameter value different from normal overload resolution? Is this case described in the ISO C++ standard?

like image 749
Jared Hoberock Avatar asked Nov 21 '12 00:11

Jared Hoberock


3 Answers

This program suggests that the set of functions considered follows normal overload resolution rules:

#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;
}

When compiled with g++-4.6, this program outputs:

$ g++ test_2.cpp ; ./a.out 
foo(type1)
foo(type2)

In other words, foo is resolved via ADL when bar is instantiated.

The behavior for the code from the OP seems to be that ADL does not apply to primitives such as int and double, so the only overloads considered are those declared before foo's call. The odd MSVC behavior seems to be non-standard.

like image 166
Jared Hoberock Avatar answered Oct 20 '22 00:10

Jared Hoberock


Here is what the standard says about this. First, in 8.3.6 [dcl.fct.default] paragraph 5:

... The names in the default argument are bound, and the semantic constraints are checked, at the point where the default argument appears. Name lookup and checking of semantic constraints for default arguments in function templates and in member functions of class templates are performed as described in 14.7.1. ...

Further in 14.7.1 [temp.inst] paragraph 12:

If a function template f is called in a way that requires a default argument to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument is done as if the default argument had been an initializer used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.

I'm not quite sure what this exactly says. I can see both interpretations in this text. In case it matters, I think EDG agrees with gcc and clang.

like image 22
Dietmar Kühl Avatar answered Oct 19 '22 23:10

Dietmar Kühl


Maybe it's because you declared&defined the overloaded function "foo(double)" after the template, so it doesn't know anything about its existence . ->structured programming. I'd suggest:

#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; 
}
like image 27
okaerin Avatar answered Oct 20 '22 00:10

okaerin