Default vs Deduced template parameter? - c++

In the following :
template<typename Type>
struct MyClass
{
template<typename OtherType> MyClass(const MyClass<OtherType>& x);
template<typename OtherType = Type> void test(const MyClass<OtherType>& x);
};
In the function test what is done between :
Case 1 : The default parameter is priority : the conversion constructor MyClass<Type>(const MyClass<OtherType>& x) is implicitely called and MyClass<Type>::test<Type>(const MyClass<Type>& x) is called.
Case 2 : The deduced parameter is priority : MyClass<Type>::test<Type>(const MyClass<OtherType>& x) is called.
I think that the good answer is the second one, but I'm not sure. Can you confirm me that (and that this situation is well-defined by the standard) ?
EDIT : The test function is called by :
MyClass<double> d;
MyClass<unsigned int> ui;
d.test(ui); // <- So the question is : is ui implicitely
// converted to MyClass<double> or not ?

test will be called as
MyClass<double>::test(const MyClass<unsigned int> &)
i.e. there will be no conversion of ui from MyClass<unsigned int> to MyClass<double>.
A default template argument never overrides a given one. It is only used when no template argument is given and the compiler can't deduce it from the function arguments.
From the C++11 Standard:
(§14.8.2/5) The resulting substituted and adjusted function type is used as the type of the function template for template argument deduction. If a template argument has not been deduced, its default template argument, if any, is used. [ Example:
template <class T, class U = double>
void f(T t = 0, U u = 0);
void g() {
f(1, ’c’); // f<int,char>(1,’c’)
f(1); // f<int,double>(1,0)
f(); // error: T cannot be deduced
f<int>(); // f<int,double>(0,0)
f<int,char>(); // f<int,char>(0,0)
}
— end example ]

Related

constructor overloading between template specialized classes

In the following code I have a class template and its specialization.
template<size_t U>
struct Foo
{
Foo(double(&u)[U]) : u{ u } {}
double(&u)[U];
};
template<>
struct Foo<1>
{
double &u;
bool specialized = true;
Foo(double &u) : u{ u } {}
};
If I attempt to create an instance with deduced template arguments, then the constructor arguments will only be matched against the generic Foo object.
double s[7] = { 1, 2, 3, 4, 5, 6, 7 };
Foo f(s); // will deduce s is of type Foo<7>
double t = 5.;
Foo g(t); // no instance matches the argument list!
Foo<1> g(t); // I must explicitly tell it I'm using the specialization
Of course, if the specialized class had the same constructor parameters, and I did something like Foo g(t) where t is type double[1], the instance would be of the specialized type.
So, how come the specialized constructor is also not resolved (or otherwise part of the set of constructors) in this case?
Only primary template is considered for Implicitly-generated deduction guides, but you can add deduction guide yourself:
Foo(double &u) -> Foo<1>;
Demo
Notice also that
double t[1] = {5.};
Foo g(t); // deduce Foo<1>, but fails as specialization doesn't have compatible constructor

Default lambda as templated parameter of a function

Consider the following code
template<bool b, typename T> void foo(const T& t = []() {}) {
// implementation here
}
void bar() {
foo<true>([&](){ /* implementation here */ }); // this compiles
foo<true>(); // this doesn't compile
}
In the case that doesn't compile I get the following errors:
error C2672: 'foo': no matching overloaded function found
error C2783: 'void foo(const T&)': could not deduce template argument for 'T'
I think it's clear what I want to achieve: let foo be called with and without a client-provided lambda. The compiler is MSVC++2017 version 15.4.4 toolset v141.
Default function arguments are not part of the template argument deduction process. To quote [temp.deduct.partial]/3:
The types used to determine the ordering depend on the context in
which the partial ordering is done:
In the context of a function call, the types used are those function parameter types for which the function call has arguments.
141
141) Default arguments are not considered to be arguments in this
context; they only become arguments after a function has been
selected.
That bullet and note indicate that since you didn't provide an argument for t in the call to foo, the type T cannot be deduced. The default lambda argument can only be taken into account if the function is selected to be called, not before.
The solution, as all the others have noted, is to provide an overload without parameters, that will call the templated one with the default lambda you have in mind.
Another (very efficient) way - default T to be a null functor.
// no_op is a function object which does nothing, regardless of how many
// arguments you give it. It will be elided completely unless you compile with
// -O0
struct no_op
{
template<class...Args>
constexpr void operator()(Args&&...) const {}
};
// foo defaults to using a default-constructed no_op as its function object
template<bool b, typename T = no_op> void foo(T&& t = T())
{
// implementation here
t();
}
void bar() {
foo<true>([&](){ std::cout << "something\n"; }); // this compiles
foo<true>(); // this now compiles
}
The compiler uses the arguments passed to deduce the template type. If there's no arguments, then how would the compiler be able to deduce the template type?
You can use overloading instead of default arguments here.
The overloaded non-argument function can simply call the function with the "default" argument:
template<bool b, typename T> void foo(const T& t) {
// implementation here
}
template<bool b> void foo() {
foo<b>([]() {});
}
Consider overloading it directly:
template <bool b>
void foo(void) {
foo([](){});
}
See CppReference:
Non-deduced contexts
4) A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done:
Type template parameter cannot be deduced from the type of a function default argument:
template void f(T = 5, T = 7);
void g()
{
f(1); // OK: calls f<int>(1, 7)
f(); // error: cannot deduce T
f<int>(); // OK: calls f<int>(5, 7)
}
You are trying to say something that makes no sense. You are asking the compiler to guess T from your arguments, but then you do not provide any argument.
The following code does compile, and does what you want:
template<bool b, typename T> void foo(const T& t) {
// implementation here
}
template<bool b> void foo() {
foo<b>([]() {}); // Call actual implementation with empty lambda
}
void bar() {
foo<true>([&](){ /* implementation here */ }); // this compiles
foo<true>(); // this now compiles as well
}

Why SFINAE doesn't work in right side in default function arguments?

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?
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
...
The type z is an int, is not being deduced by the compiler, no room for SFINAE to take place. The value being used to initialise z is based on the default of T::foo, which doesn't exist; hence the error.
If the type for z is elevated to the template itself, substitution can now fail, and SFINAE kicks in.
#include <iostream>
struct My
{
typedef int foo;
};
struct My2
{
};
template <typename T, typename I = typename T::foo>
void Bar(const T&, I z = I())
{
(void)z; // silence any warnings on unused
std::cout << "My" << std::endl;
}
void Bar(...)
{
std::cout << "..." << std::endl;
}
int main()
{
My my;
Bar(my);
My2 my2;
Bar(my2); // Compiles
return 0;
}
Live sample
In order for a function template to be part of the overloaded list of candidate functions, the template argument deduction must succeed. If it fails, then the candidate is removed from the list. Hence, if no deduction failure occurs, it is added to the candidate list (but this does not preclude further errors if it is finally selected).
14.8.3/1 Overload resolution
A function template can be overloaded either by (non-template) functions of its name or by (other) function templates of the same name. When a call to that name is written (explicitly, or implicitly using the operator notation), template argument deduction (14.8.2) and checking of any explicit template arguments (14.3) are performed for each function template to find the template argument values (if any) that can be used with that function template to instantiate a function template specialization that can be invoked with the call arguments. For each function template, if the argument deduction and checking succeeds, the template arguments (deduced and/or explicit) are used to synthesize the declaration of a single function template specialization which is added to the candidate functions set to be used in overload resolution. If, for a given function template, argument deduction fails, no such function is added to the set of candidate functions for that template. The complete set of candidate functions includes all the synthesized declarations and all of the non-template overloaded functions of the same name. The synthesized declarations are treated like any other functions in the remainder of overload resolution, except as explicitly noted in 13.3.3.
Template argument deduction is performed on the function type and its template arguments themselves.
14.8.2/8 Template argument deduction
If a substitution results in an invalid type or expression, type deduction fails. An invalid type or expression is one that would be ill-formed, with a diagnostic required, if written using the substituted arguments. [ Note: If no diagnostic is required, the program is still ill-formed. Access checking is done as part of the substitution process. —end note ] Only invalid types and expressions in the immediate context of the function type and its template parameter types can result in a deduction failure.
From the OP, the function Bar<T> is added to the candidate list since it can be deduced what the type for T is. It is instantiated and the default arguments are checked, and hence it fails.
14.7.1/13 Implicit instantiation
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.
Quotes taken from draft n3797
One more C++03 compatible option for you. Because in answers above default argument was used in template function and it is not permitted in standard.
#include <iostream>
struct TypeWithFoo{
typedef int Foo;
};
template<typename T, bool>
struct onFooAction;
template<typename T>
struct onFooAction<T, false>{
void operator ()(const T &t){
std::cout << "No foo :(\n";
}
};
template<typename T>
struct onFooAction<T, true>{
void operator ()(const T &t){
std::cout << "Foo =)\n";
}
};
template<typename T>
struct hasFoo{
typedef char yes[1];
typedef char no[2];
template<typename C>
static yes& testForFoo(typename C::Foo*);
template<typename>
static no& testForFoo(...);
static const bool value = sizeof(testForFoo<T>(0)) == sizeof(yes);
};
template<typename T>
void bar(const T &t){
onFooAction<T, hasFoo<T>::value>()(t);
}
int main(){
bar(10);
bar(TypeWithFoo());
}

Is it possible to have functions generated from template as f() and f<T>()?

Is it possible to have functions generated from template as f() and f<T>() ?
I want to call f most of the time with the type specified, such as:
f<string>();
f<int>();
but I also need to call it like this:
f();
and the type when not specified it should be string. Is this possible?
template <typename T>
void f() { ... }
void f() { f<string>(); }
You can give default type for template arguments:
template<class T=std::string>
foo()
Note: If you give default argument to template class, you must declare the default version with Foo<>. This is not necessary when calling templated functions; You can call the default version without angle brackets: foo()
Another note: This works for functions because of template argument deduction. Quote from the standard (January 2012 draft §14.8.2.5) emphasis mine:
The resulting substituted and adjusted function type is used as the
type of the function template for template argument deduction. If a
template argument has not been deduced, its default template argument,
if any, is used. [ Example:
template <class T, class U = double>
void f(T t = 0, U u = 0);
void g() {
f(1, ’c’); //f<int,char>(1,’c’)
f(1); //f<int,double>(1,0)
f(); //error: T cannot be deduced
f<int>(); //f<int,double>(0,0)
f<int,char>(); //f<int,char>(0,0)
}
#include <string>
template <typename T=std::string>
void f() {}
int main()
{
f();
}

Type deduction fails with pointer to member method

I have the following template class which acts as a proxy. It has a method named call which is supposed to be used to call methods on the wrapped object. There's a problem with it. The type deduction fails and I cannot understand why.
Hudsucker::f takes an std::string and then no matter if I pass an std::string or a const reference to it the compiler is able to call the right method.
But in case of Hudsucker::g with takes a const reference to std::string type deduction fails in both cases with both GCC and Clang.
GCC error for the first line:
main.cpp:36:28: error: no matching function for call to ‘Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&)’
main.cpp:36:28: note: candidate is:
main.cpp:10:10: note: template<class A> void Proxy::call(void (T::*)(A), A) [with A = A; T = Hudsucker]
main.cpp:10:10: note: template argument deduction/substitution failed:
main.cpp:36:28: note: deduced conflicting types for parameter ‘A’ (‘const std::basic_string<char>&’ and ‘std::basic_string<char>’)
Especially this bit is strange: no matching function for call to Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&). That is exactly the signature I would expect to see work.
Clang error for the first line:
main.cpp:36:7: error: no matching member function for call to 'call'
p.call(&Hudsucker::g, s); // <- Compile error
~~^~~~
main.cpp:10:10: note: candidate template ignored: deduced conflicting types for parameter 'A' ('const std::basic_string<char> &' vs. 'std::basic_string<char>')
void call(void (T::*f)(A), A a)
Code:
#include <string>
#include <iostream>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A>
void call(void (T::*f)(A), A a)
{
(o_.*f)(a);
}
private:
T &o_;
};
class Hudsucker
{
public:
void f(std::string s) {}
void g(std::string const &s) {}
};
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
p.call(&Hudsucker::g, s); // <- Compile error
p.call(&Hudsucker::g, r); // <- Compile error
return 0;
}
Could you explain why the type deduction fails in that way? Is there a way to get this to compile with const references?
The compiler cannot deduce the type A, since it has contrasting information. From the type of the member function, it would deduce A to be std::string const&, while from the type of the second argument, it would deduce it to be std::string.
Change your function template into one that allows different types for the parameter of the member function and the argument actually provided, and then SFINAE-constrain the latter to be convertible to the former:
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
If you are wondering why from this function call:
std::string const s = "For kids, you know.";
// ...
p.call(&Hudsucker::g, s);
The compiler would deduce std::string, that's because of paragraph 14.8.2.1/2 of the C++11 Standard:
If P is not a reference type:
— If A is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is
used in place of A for type deduction; otherwise,
— If A is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3)
is used in place of A for type deduction; otherwise,
— If A is a cv-qualified type, the top level cv-qualifiers of A’s type are ignored for type deduction.
In the quoted paragraph, P is your A (from your function template) and A is std::string const. This means the const in std::string const is ignored for type deduction. To see this better, consider this simpler example:
#include <type_traits>
template<typename T>
void foo(T t)
{
// Does NOT fire!
static_assert(std::is_same<T, int>::value, "!");
}
int main()
{
int const x = 42;
foo(x);
}
Considering the second function call:
std::string const &r = s;
// ...
p.call(&Hudsucker::g, r);
The reason is that the type of the id-expression r is std::string const. The reference is dropped because of paragraph 5/5:
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to
any further analysis. The expression designates the object or function denoted by the reference, and the
expression is an lvalue or an xvalue, depending on the expression.
And now we're back to the same situation as for the first function call.
As pointed out by Mike Vine in the comments, you may want to perfectly-forward your second argument when giving it in input to the first (member function) argument during the function call:
#include <utility> // For std::forward<>()
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B&& a)
{
(o_.*f)(std::forward<B>(a));
}
If you cannot afford C++11, then you won't be allowed to use default arguments for template parameters. In that case, you can use the SFINAE-constraint on the return type:
template <typename A, typename B>
typename enable_if<is_convertible<B, A>::value>::type
// ^^^^^^^^^ ^^^^^^^^^^^^^^
// But how about these traits?
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
Notice, that std::enable_if and std::is_convertible are not part of the C++03 Standard Library. Fortunately, Boost has its own version of enable_if and is_convertible, so:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_convertible.hpp>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A, typename B>
typename boost::enable_if<boost::is_convertible<B, A>>::type
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
private:
T &o_;
};
Notice, that boost::enable_if accepts as its first template argument a type which defines a value boolean member, whereas std::enable_if accepts a boolean value. The equivalent of std::enable_if in Boost is boost::enable_if_c.
Seems to me a simpler solution would be to just exclude one of the two arguments from trying to deduce A, and the second one is the better candidate:
template <typename A>
void call(void (T::*f)(A), typename std::identity<A>::type a)
{
(o_.*f)(a);
}
If you don't have std::identity in your type traits, use this one:
template <typename T>
struct identity { typedef T type; };
Here's why this works: the compiler cannot deduce A from the second argument, since it's just a template parameter to something that a nested type is taken of. Basically, it can't pattern-match any incoming type against something_that_contains_A::type - due to template specialization, it can't reverse-engineer the argument from the definition of the left side. The net result is that the second argument is an "undeduced context". The compiler will not attempt to deduce A from there.
This leaves the first argument as the only place where A can be deduced from. With only one deduction result for A, it is not ambiguous and deduction succeeds. The compiler then proceeds to substitute the deduction result into every place where A was used, including the second argument.
You just need to pass template argument to template function when calling it in your main.
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
//just add template argument to template function call !!!
p.call< const std::string & > (&Hudsucker::g, s); // <- NO Compile error !!!!
p.call< const std::string & > (&Hudsucker::g, r); // <- NO Compile error !!!**
return 0;
}