I have this code:
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T>
void Bar(const T&, int z = typename T::foo())
{
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Compile error: no type named ‘foo’ in ‘struct My2’
return 0;
}
I suppose, that if some class T doesn't have typedef foo inside, compiler should exclude first overload and choose overload with ellipsis. But I check this code on MSVC, gcc and clang and I get compile error on those compilers. Why SFINAE doesn't work in this case?
Characteristics for defining the default argumentsThe values passed in the default arguments are not constant. These values can be overwritten if the value is passed to the function. If not, the previously declared value retains. During the calling of function, the values are copied from left to right.
Default Arguments in C++ A default argument is a value provided in a function declaration that is automatically assigned by the compiler if the calling function doesn't provide a value for the argument. In case any value is passed, the default value is overridden.
No you cannot overload functions on basis of value of the argument being passed, So overloading on the basis of value of default argument is not allowed either.
Default arguments are only allowed in the parameter lists of function declarations and lambda-expressions, (since C++11) and are not allowed in the declarations of pointers to functions, references to functions, or in typedef declarations.
The type of z
is not subject to template substitution, it is always int
. This means there is no opportunity for SFINAE, and you instead get a compiler error when attempting to resolve T::foo
for the default value. Default arguments do not participate in overload resolution, instead being instantiated only when missing from the function call. Section 14.7.1 (paragraphs 13/14) of the standard describes this behaviour, but does not give justification for the lack of SFINAE here.
SFINAE can be allowed to happen by making the type of z
a template parameter, as below:
(live example: http://ideone.com/JynMye)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
void Bar(...)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
This will use the "My" version for the first call, and the "..." version for the second call. The output is
My
...
However, if void Bar(...) was a template, for whatever reason, the "My" version will never get a chance:
(live example: http://ideone.com/xBQiIh)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=typename T::foo> void Bar(const T&, I z = I())
{
std::cout << "My\n";
}
template<typename T> void Bar(T&)
{
std::cout << "...\n";
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Also OK
return 0;
}
Here, the "..." version is called in both cases. The output is:
...
...
One solution is to use class template (partial) specialisation; provide the "..." version as the base, with the type of the second parameter defaulted to int
, and the "My" version as a specialisation where the second parameter is typename T::foo
. In conjunction with a plain template function to deduce T and dispatch to the appropriate class' member function, this produces the desired effect:
(live example: http://ideone.com/FanLPc)
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template<typename T, typename I=int> struct call_traits {
static void Bar(...)
{
std::cout << "...\n";
}
};
template<typename T> struct call_traits<T, typename T::foo> {
static void Bar(const T&, int z=typename T::foo())
{
std::cout << "My\n";
}
};
template<typename T> void Bar(const T& t)
{
call_traits<T>::Bar(t);
}
int main()
{
My my;
Bar(my); // OK
My2 my2;
Bar(my2); // Still OK
return 0;
}
Here, the output is:
My
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With