Consider this example of code:
#include <iostream>
#include <functional>
typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;
struct X
{
X (func1_t f)
{ }
X (func2_t f)
{ }
};
int main ( )
{
X x([](){ std::cout << "Hello, world!\n"; });
}
I was sure that it shouldn't compile, because the compiler shouldn't be able to choose one of the two constructors. g++-4.7.3 shows this expected behavior: it says that call of overloaded constructor is ambiguous. However, g++-4.8.2 successfully compiles it.
Is this code correct in C++11 or it is a bug/feature of this version of g++?
In C++11...
Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type
R. The copy constructor and destructor of A shall not throw
exceptions.
8 Postconditions: !*this if any of the following hold:
f is a NULL function pointer.
f is a NULL pointer to member.
F is an instance of the function class template, and !f
9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]
10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw
bad_alloc or any exception thrown by F’s copy or move constructor.
Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.
[res.on.required]/1
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.
In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible.
8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.
[...]
The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
and a call-expression such as
foo([](std::string){}) // (C)
then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string.
For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.
Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.
How can SFINAE help here?
Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
It is totally valid. Since c++11 lambda expressions (and your std::function wrapper) create function objects. The great strength of function objects is that, even when they are generic, they remain first-class objects. Unlike ordinary function templates, they can be passed to and returned from functions.
You can create operator overload sets explicitly with inheritance and using declarations. The following usage from Mathias Gaunard demonstrates “overloaded lambda expressions".
template <class F1, class F2>
struct overload_set : F1, F2
{
overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
using F1::operator();
using F2::operator();
};
template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
return overload_set<F1,F2>(x1,x2);
}
auto f = overload(
[](){return 1;},
[](int x){return x+1;}
);
int x = f();
int y = f(2);
source
EDIT: Maybe it'll become more clear if in the provided example you replace
F1 -> std::function<void()>
F2 -> std::function<void(int)>
and see it compile in gcc4.7
The templated solution was only provide to demonstrate that concept scales to generic code and dissambiguation is possible.
In your case, when using an older compiler like gcc 4.7, you could help by explicit cast and gcc will work things out, as you can see in this live example
Just in case you're wondering, it wouldn't work if you cast the other way around (try to convert the lambda taking int to the std::function taking no arguments and so on)
Related
I am curious as to why a2 is fine, but b2 won't compile:
#include <functional>
#include <iostream>
class A {
public:
A(std::function<void(int)>&& func) : f(std::move(func)) {}
std::function<void(int)> f;
};
template <class F>
class B {
public:
B(F&& func) : f(std::move(func)) {}
F f;
};
int main() {
int a = 1;
auto f = [a](int b){std::cout << a+b << '\n';};
A a1([a](int b){std::cout << a+b << '\n';});
A a2(f);
B b1([a](int b){std::cout << a+b << '\n';});
// B b2(f);
a1.f(2);
a2.f(2);
b1.f(2);
// b2.f(2);
f(2);
}
A(std::function<void(int)>&& func)
A can be initialized with std::function rvalue. Now, f (in main) is not a std::function, seeing as each lambda has its own distinct type. But we can create a temporary std::function out of it, and bind the rvalue reference func to that.
B(F&& func)
Don't let appearances fool you. This may look like a forwarding reference, but it isn't. Forwarding references are syntactically rvalue reference to a template parameter, but it must be a template parameter of the function we are forwarding into.
The constructor of B is not a template, and so func is not a forwarding reference.
The deduction guide that gets generated from that constructor accepts only rvalues, and deduces F from that. Because f (the local lambda in main) is an lvalue, it cannot bind to an rvalue reference and so CTAD cannot succeed. Indeed std::move(f) will make b2 well-formed.
If you want to accept lvalues for arguments as well, you may add another constructor
B(F const& func) : f(func) {}
Now there are two deduction guides being generated, one for each value category.
B b2(f);
B is not a class, or a type, it is a template. An object declaration in C++ is, basically a declaration: here's a type, and here's one or more objects of that type (or a derived type). A template is not a type. A template instantiation is a type.
std::vector b2;
This won't compile for the same reason your code will not compile. You have to specify the template parameter to instantiate a type, such as:
std::vector<int> b2;
The same reason explains your compilation error.
Having said that, a small change to your template will make it compile as long as your compiler supports C++17:
template <class F>
class B {
public:
B(F func) : f(std::move(func)) {}
F f;
};
A C++17 compiler will be able to deduce the template parameter due to class template deduction guides in C++17. And, it's possible that with a little bit of additional work (I haven't looked into it) it might be possible to fiddle a few things and make your original template also work in C++17.
The following code:
struct A
{
using this_t = A;
template <class F1>
void ins(const F1& fu)
{
}
template <class F1, class... Args>
void ins(const F1& fu, const Args&... args)
{
this_t::ins<Args...>(args...);
// the line which is causing the error:
constexpr decltype(&this_t::ins<Args...>) func = &this_t::ins<Args...>;
}
};
int main()
{
A a;
a.ins(5, 5, 5, 5, 5);
}
Gives this error:
error: reference to overloaded function could not be resolved
As shown here: https://godbolt.org/z/nJiY4A
What's going wrong here and how is it possible to get the pointer to the function then?
I think I know why that line fails to compile. It has little to do with overload resolution, and everything to do with decltype:
[over.over]/1 A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. A function with type F is selected for the function type FT of the target type required in the context if F (after possibly applying the function pointer conversion (7.13)) is identical to FT. The target can be
(1.1) — an object or reference being initialized (11.6, 11.6.3, 11.6.4),
(1.2) — the left side of an assignment (8.18),
(1.3) — a parameter of a function (8.2.2),
(1.4) — a parameter of a user-defined operator (16.5),
(1.5) — the return value of a function, operator function, or conversion (9.6.3),
(1.6) — an explicit type conversion (8.2.3, 8.2.9, 8.4), or
(1.7) — a non-type template-parameter (17.3.2).
The overloaded function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed.
Emphasis mine. Just mentioning an overloaded function name outside of those enumerated contexts - e.g. in decltype(&this_t::ins<F2, Args...>) - renders the program ill-formed. Avoiding this problematic usage by spelling the type explicitly makes the code compile:
void (this_t::*func)(const Args&...) = &this_t::ins<Args...>;
I'm not yet sure why taking an address of the overloaded function behaves differently than calling it; why the former produces an ambiguity while the latter is able to resolve the overload successfully.
Meanwhile, here's one possible workaround:
struct A
{
using this_t = A;
template <class F1>
void ins(const F1& fu)
{
}
template <class F1, class F2, class... Args>
void ins(const F1& f1, const F2& f2, const Args&... args)
{
this_t::ins<F2, Args...>(f2, args...);
// the line which is causing the error:
constexpr decltype(&this_t::ins<F2, Args...>) func = &this_t::ins<F2, Args...>;
}
};
The tweak is to make the first overload callable with one argument, and the second with two or more. Demo
Not exactly what you asked but... if you can use an empty (do nothing, nothing receiving) ground recursion case, I propose the following solution
struct A
{
using this_t = A;
template <int = 0>
void ins ()
{ }
template <typename F1, typename ... Args>
void ins (F1 const &, Args const & ... args)
{
this_t::ins<Args...>(args...);
// no more error
constexpr decltype(&this_t::ins<Args...>) func = &this_t::ins<Args...>;
}
};
This way, calling ins<Args...>, you can avoid all ambiguities when Args... is only one type (only the recursion case matches) and when Args... is empty, matches the ground case thanks to the int = 0 template default value.
Universal references (i.e. "forward references", the c++ standard name) and perfect forwarding in c++11, c++14, and beyond have many important advantages; see here, and here.
In Scott Meyers' article referenced above (link), it is stated as a rule of thumb that:
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
Example 1
Indeed, using clang++ we see that the following code snippet will successfully compile with -std=c++14:
#include <utility>
template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}
int x1 = 1;
int const x2 = 1;
int& x3 = x1;
int const& x4 = x2;
// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1); // various lvalues okay, as expected
auto r2 = f (x2); // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected
Given any description of universal references (forward references) and type deduction (see, for instance, this explanation) it is clear why the above works. Although, from the same explanation, it is not abundantly clear why the below fails to work as well.
(failed) Example 2
This question addresses the same issue. The provided answers do not, however, explain why templated types are not categorized as being "deduced".
What I am about to show (seemingly) satisfies the requirement stated above by Meyers. However, the following code snipped fails to compile, producing the error (among others for each call to f):
test.cpp:23:11: error: no matching function for call to 'f'
auto r1 = f (x1);
test.cpp:5:16: note: candidate function [with T = foo, A = int] not
viable: no known conversion from 'struct foo< int >' to 'foo< int > &&'
for 1st argument
decltype(auto) f (T< A > && t)
#include <utility>
//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works
In context, since the type T<A> of f's parameter is deduced, surely the parameter declaration T<A>&& t would behave as a universal reference (forward reference).
Example 3 (for clarity in describing the problem at hand)
Let me stress the following: the failure of the code in Example 2 to compile is not due to the fact that struct foo<> is a templated type. The failure seems to be cause only by the declaration of f's parameter as a templated type.
Consider the following revision to the previous code, which now does compile:
#include <utility>
//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}
//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};
struct foo<int> x1 { .bar = 1 };
struct foo<int> const x2 { .bar = 1 };
struct foo<int> & x3 = x1;
struct foo<int> const& x4 = x2;
// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
It is astonishing to me that this simple change completely alters the behaviour of the type deduction for the template function f's type parameter.
Questions:
Why does the second example not work as expected? Are there techniques to overcome this problem with templated types in c++11/14? Are there well known, extant codebases (in the wild) making successful use of c++'s forward references with templated types?
When you call some function f with some lvalue:
int a = 42;
f(a);
Then f must be able to accept such an lvalue. This is the case when the first parameter of f is a (lvalue) reference type, or when it's not a reference at all:
auto f(int &);
auto f(int); // assuming a working copy constructor
This won't work when the parameter is a rvalue reference:
auto f(int &&); // error
Now, when you define a function with a forwarding reference as first parameter as you did in the first and third example ...
template<typename T>
auto f(T&&); // Showing only declaration
... and you actually call this function with an lvalue, template type deduction turns T into an (lvalue) reference (that this happens can be seen in the example code I provide in a moment):
auto f(int & &&); // Think of it like that
Surely, there are too much references involved above. So C++ has collapsing rules, which are actually quite simple:
T& & becomes T&
T& && becomes T&
T&& & becomes T&
T&& && becomes T&&
Thanks to the second rule, the "effective" type of the first parameter of f is a lvalue reference, so you can bind your lvalue to it.
Now when you define a function g like ...
template<template<class> class T, typename A>
auto g(T<A>&&);
Then no matter what, template parameter deduction must turn the T into a template, not a type. After all, you specified exactly that when declaring the template parameter as template<class> class instead of typename.
(This is an important difference, foo in your example is not a type, it's a template ... which you can see as type level function, but back to the topic)
Now, T is some kind of template. You cannot have a reference to a template.
A reference (type) is built from a (possibly incomplete) type. So no matter what, T<A> (which is a type, but not a template parameter which could be deduced) won't turn into an (lvalue) reference, which means T<A> && doesn't need any collapsing and stays what it is: An rvalue reference. And of course, you cannot bind an lvalue to an rvalue reference.
But if you pass it an rvalue, then even g will work.
All of the above can be seen in the following example:
template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};
f(thing<int> {}); // "not a reference"
f(it); // "a reference"
// T = thing<int> &
// T&& = thing<int>& && = thing<int>&
g(thing<int> {}); // works
//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&
return 0;
}
(Live here)
Concerning how one could "overcome" this: You cannot. At least not the way you seem to want it to, because the natural solution is the third example you provide: Since you don't know the type passed (is it an lvalue reference, a rvalue reference or a reference at all?) you must keep it as generic as T. You could of course provide overloads, but that would somehow defeat the purpose of having perfect forwarding, I guess.
Hm, turns out you actually can overcome this, using some traits class:
template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};
You can then extract both the template and the type the template was instantiated with inside of the function:
template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}
(Live here)
[...] can you explain why it is not an universal reference? what would the danger or the pitfall of it be, or is it too difficult to implement? I am sincerely interested.
T<A>&& isn't an universal reference because T<A> isn't a template parameter. It's (after deduction of both T and A) a simple (fixed / non generic) type.
A serious pitfall of making this a forwarding reference would be that you could no longer express the current meaning of T<A>&&: An rvalue reference to some type built from the template T with parameter A.
Why does the second example not work as expected?
You have two signatures:
template <typename T>
decltype(auto) f (T&& );
template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );
f takes a forwarding reference, but f2 does not. The specific rule, from [temp.deduct.call] is, bold emphasis mine:
A forwarding reference is an rvalue
reference to a cv-unqualified template parameter. If P is a forwarding reference and the argument is an
lvalue, the type “lvalue reference to A” is used in place of A for type deduction.
With f, the argument is an rvalue reference to a template parameter (T). But with f2, T<A> is not a template parameter. Thus, that function simply takes as its argument an rvalue reference to T<A>. The calls fail to compile because all your arguments are lvalues and this case has no special exception for being called with an lvalue.
As for overcoming the problem, I guess a more or less equivalent way would be to deduce it with a forward reference, and trigger the comparison with T<A> manually.
template<typename T>
class X;
template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;
template<typename _>
using template_ = T<_>;
};
template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};
template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}
If you don't actually want T<A> but a specific type, the best way is to use std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>, which is way easier, I guess.
int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}
It is not enough to have type deduction. The form of the type declaration must be exactly T&& (an rvalue reference to just a template parameter). If it's not (or there is no type deduction) the parameter is an rvalue reference. If the argument is an lvalue, it won't compile. Since T<A>&& does not have that form, f (T<A> && t) is unable to accept an lvalue (as an lvalue reference) and you get the error. If you think that requires too much generality, consider that a simple const qualifier breaks it too:
template<typename T>
void f(const T&& param); // rvalue reference because of const
(putting aside the relative uselessness of a const rvalue reference)
The rules for reference collapsing simply do not kick in unless the most general form T&& is used. Without the ability for f to recognize an lvalue argument was passed and treat the parameter as an lvalue reference, there is no reference collapsing to be done (i.e. collapsing T& && to T& can't happen and it's just T<something>&&, an rvalue ref. to a templated type). The needed mechanism for the function to determine whether an rvalue or an lvalue is passed as an argument is encoded in the deduced template parameter. However, this encoding only occurs for a universal reference parameter, as strictly defined.
Why is this level of generality is necessary (besides just being the rule)? Without this specific definition format, universal references could not be super-greedy functions that instantiate to capture any type of argument... as they are designed to be. Daniel's answer gets to the point, I think: Suppose you want to define a function with a regular rvalue reference to a templated type parameter, T<A>&& (i.e. that does not accept an lvalue argument). If the following syntax were treated as a universal reference, then how would you change it to specify a regular rvalue reference?
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?
There needs to be a way to explicitly define the parameter as an rvalue reference to exclude lvalue arguments. This reasoning would seem to apply for other types of parameters, including cv qualifications.
Also, there seem to be ways around this (see traits and SFINAE), but I can't answer that part. :)
Consider this example of code:
#include <iostream>
#include <functional>
typedef std::function<void()> func1_t;
typedef std::function<void(int)> func2_t;
struct X
{
X (func1_t f)
{ }
X (func2_t f)
{ }
};
int main ( )
{
X x([](){ std::cout << "Hello, world!\n"; });
}
I was sure that it shouldn't compile, because the compiler shouldn't be able to choose one of the two constructors. g++-4.7.3 shows this expected behavior: it says that call of overloaded constructor is ambiguous. However, g++-4.8.2 successfully compiles it.
Is this code correct in C++11 or it is a bug/feature of this version of g++?
In C++11...
Let's take a look at the specification of the constructor template of std::function (which takes any Callable): [func.wrap.func.con]/7-10
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible. f shall be Callable (20.10.11.2) for argument types ArgTypes and return type
R. The copy constructor and destructor of A shall not throw
exceptions.
8 Postconditions: !*this if any of the following hold:
f is a NULL function pointer.
f is a NULL pointer to member.
F is an instance of the function class template, and !f
9 Otherwise, *this targets a copy of f initialized with std::move(f). [left out a note here]
10 Throws: shall not throw exceptions when f is a function pointer or a reference_wrapper<T> for some T. Otherwise, may throw
bad_alloc or any exception thrown by F’s copy or move constructor.
Now, constructing, or attempting to construct (for overload resolution) a std::function<void(int)> from a [](){} (i.e. with signature void(void)) violates the requirements of std::function<void(int)>'s constructor.
[res.on.required]/1
Violation of the preconditions specified in a function’s Requires: paragraph results in undefined behavior unless the function’s Throws: paragraph specifies throwing an exception when the precondition is violated.
So, AFAIK, even the result of the overload resolution is undefined. Therefore, both versions of g++/libstdc++ are complying in this aspect.
In C++14, this has been changed, see LWG 2132. Now, the converting constructor template of std::function is required to SFINAE-reject incompatible Callables (more about SFINAE in the next chapter):
template<class F> function(F f);
template <class F, class A> function(allocator_arg_t, const A& a, F f);
7 Requires: F shall be CopyConstructible.
8 Remarks: These constructors shall not participate in overload
resolution unless f is Callable (20.9.11.2) for argument types
ArgTypes... and return type R.
[...]
The "shall not participate in overload resolution" corresponds to rejection via SFINAE. The net effect is that if you have an overload set of functions foo,
void foo(std::function<void(double)>);
void foo(std::function<void(char const*)>);
and a call-expression such as
foo([](std::string){}) // (C)
then the second overload of foo is chosen unambiguously: Since std::function<F> defines F as its interface to the outside, the F defines which argument types are passed into std::function. Then, the wrapped function object has to be called with those arguments (argument types). If a double is passed into std::function, it cannot be passed on to a function taking a std::string, because there's no conversion double -> std::string.
For the first overload of foo, the argument [](std::string){} is therefore not considered Callable for std::function<void(double)>. The constructor template is deactivated, hence there's no viable conversion from [](std::string){} to std::function<void(double)>. This first overload is removed from the overload set for resolving the call (C), leaving only the second overload.
Note that there's been a slight change to the wording above, due to LWG 2420: There's an exception that if the return type R of a std::function<R(ArgTypes...)> is void, then any return type is accepted (and discarded) for the Callable in the constructor template mentioned above. For example, both []() -> void {} and []() -> bool {} are Callable for std::function<void()>. The following situation therefore produces an ambiguity:
void foo(std::function<void()>);
void foo(std::function<bool()>);
foo([]() -> bool {}); // ambiguous
The overload resolution rules don't try to rank among different user-defined conversions, and hence both overloads of foo are viable (first of all) and neither is better.
How can SFINAE help here?
Note when a SFINAE-check fails, the program isn't ill-formed, but the function isn't viable for overload resolution. For example:
#include <type_traits>
#include <iostream>
template<class T>
auto foo(T) -> typename std::enable_if< std::is_integral<T>::value >::type
{ std::cout << "foo 1\n"; }
template<class T>
auto foo(T) -> typename std::enable_if< not std::is_integral<T>::value >::type
{ std::cout << "foo 2\n"; }
int main()
{
foo(42);
foo(42.);
}
Similarly, a conversion can be made non-viable by using SFINAE on the converting constructor:
#include <type_traits>
#include <iostream>
struct foo
{
template<class T, class =
typename std::enable_if< std::is_integral<T>::value >::type >
foo(T)
{ std::cout << "foo(T)\n"; }
};
struct bar
{
template<class T, class =
typename std::enable_if< not std::is_integral<T>::value >::type >
bar(T)
{ std::cout << "bar(T)\n"; }
};
struct kitty
{
kitty(foo) {}
kitty(bar) {}
};
int main()
{
kitty cat(42);
kitty tac(42.);
}
It is totally valid. Since c++11 lambda expressions (and your std::function wrapper) create function objects. The great strength of function objects is that, even when they are generic, they remain first-class objects. Unlike ordinary function templates, they can be passed to and returned from functions.
You can create operator overload sets explicitly with inheritance and using declarations. The following usage from Mathias Gaunard demonstrates “overloaded lambda expressions".
template <class F1, class F2>
struct overload_set : F1, F2
{
overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
using F1::operator();
using F2::operator();
};
template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
return overload_set<F1,F2>(x1,x2);
}
auto f = overload(
[](){return 1;},
[](int x){return x+1;}
);
int x = f();
int y = f(2);
source
EDIT: Maybe it'll become more clear if in the provided example you replace
F1 -> std::function<void()>
F2 -> std::function<void(int)>
and see it compile in gcc4.7
The templated solution was only provide to demonstrate that concept scales to generic code and dissambiguation is possible.
In your case, when using an older compiler like gcc 4.7, you could help by explicit cast and gcc will work things out, as you can see in this live example
Just in case you're wondering, it wouldn't work if you cast the other way around (try to convert the lambda taking int to the std::function taking no arguments and so on)
At http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx, the VC++ team officially declare that they have not yet implemented the C++11 core feature "Expression SFINAE". However, The following code examples copied from http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2634.html are accepted by the VC++ compiler.
example 1:
template <int I> struct A {};
char xxx(int);
char xxx(float);
template <class T> A<sizeof(xxx((T)0))> f(T){}
int main()
{
f(1);
}
example 2:
struct X {};
struct Y
{
Y(X){}
};
template <class T> auto f(T t1, T t2) -> decltype(t1 + t2); // #1
X f(Y, Y); // #2
X x1, x2;
X x3 = f(x1, x2); // deduction fails on #1 (cannot add X+X), calls #2
My question is: What is "Expression SFINAE"?
Expression SFINAE is explained quite well in the paper you linked, I think. It's SFINAE on expressions. If the expression inside decltype isn't valid, well, kick the function from the VIP lounge of overloads. You can find the normative wording at the end of this answer.
A note on VC++: They didn't implement it completely. On simple expressions, it might work, but on others, it won't. See a discussion in the comments on this answer for examples that fail. To make it simple, this won't work:
#include <iostream>
// catch-all case
void test(...)
{
std::cout << "Couldn't call\n";
}
// catch when C is a reference-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c.*f)(), void()) // 'C' is reference type
{
std::cout << "Could call on reference\n";
}
// catch when C is a pointer-to-class type and F is a member function pointer
template<class C, class F>
auto test(C c, F f) -> decltype((c->*f)(), void()) // 'C' is pointer type
{
std::cout << "Could call on pointer\n";
}
struct X{
void f(){}
};
int main(){
X x;
test(x, &X::f);
test(&x, &X::f);
test(42, 1337);
}
With Clang, this outputs the expected:
Could call with reference
Could call with pointer
Couldn't call
With MSVC, I get... well, a compiler error:
1>src\main.cpp(20): error C2995: ''unknown-type' test(C,F)' : function template has already been defined
1> src\main.cpp(11) : see declaration of 'test'
It also seems that GCC 4.7.1 isn't quite up to the task:
source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = X*; F = void (X::*)()]':
source.cpp:29:17: required from here
source.cpp:11:6: error: cannot apply member pointer 'f' to 'c', which is of non-class type 'X*'
source.cpp: In substitution of 'template decltype ((c.*f(), void())) test(C, F) [with C = int; F = int]':
source.cpp:30:16: required from here
source.cpp:11:6: error: 'f' cannot be used as a member pointer, since it is of type 'int'
A common use of Expression SFINAE is when defining traits, like a trait to check if a class sports a certain member function:
struct has_member_begin_test{
template<class U>
static auto test(U* p) -> decltype(p->begin(), std::true_type());
template<class>
static auto test(...) -> std::false_type;
};
template<class T>
struct has_member_begin
: decltype(has_member_begin_test::test<T>(0)) {};
Live example. (Which, surprisingly, works again on GCC 4.7.1.)
See also this answer of mine, which uses the same technique in another environment (aka without traits).
Normative wording:
§14.8.2 [temp.deduct]
p6 At certain points in the template argument deduction process it is necessary to take a function type that makes use of template parameters and replace those template parameters with the corresponding template arguments. This is done at the beginning of template argument deduction when any explicitly specified template arguments are substituted into the function type, and again at the end of template argument deduction when any template arguments that were deduced or obtained from default arguments are substituted.
p7 The substitution occurs in all types and expressions that are used in the function type and in template parameter declarations. The expressions include not only constant expressions such as those that appear in array bounds or as nontype template arguments but also general expressions (i.e., non-constant expressions) inside sizeof, decltype, and other contexts that allow non-constant expressions.
p8 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 if written using the substituted arguments. [...]