it looks like my clang compiler (3.3) doesn't generate any errors, if the lambda's return type doesn't match:
#include <functional>
typedef std::function<void()> voidFunc;
void foo(voidFunc func)
{
func();
}
int main()
{
int i = 42;
foo([i]()
{
return i;
});
return 0;
}
Compiling this code doesn't show any errors:
clang++ -c -Xclang -stdlib=libc++ -std=c++11 -Weverything -Wno-c++98-compat -Wno-missing-prototypes -o foo.o foo.cpp
How can I generate type errors for problems like this?
EDIT:
This generates a type error:
#include <functional>
struct A {};
struct B {};
typedef std::function<A()> aFunc;
void foo(aFunc func)
{
func();
}
int main()
{
int i = 42;
foo([i]()
{
return B();
});
return 0;
}
This is the error:
foo2.cpp:16:2: error: no matching function for call to 'foo'
foo([i]() {
^~~
foo2.cpp:8:6: note: candidate function not viable: no known conversion from '<lambda at foo2.cpp:16:6>' to 'aFunc' (aka 'function<A ()>') for 1st argument
void foo(aFunc func)
^
1 error generated.
std::function is a polymorphic container for callable objects that support a particular call signature†.
The lambda in the example can be called the same way a void() function is called; ignoring return values was never a type error in C++ (whether that is a good or a bad idea is a different question).
Therefore, std::function<void()> allows such an object. The first program shown is perfectly valid.
The lambda in the second program, however, cannot be called anywhere a A() function could:
void f(A const&);
f(the_lambda()); // nope!
So the second program is not valid, and the compiler correctly reports that.
If you want a type error in a situation like this, you need to do your own type checking. In this case, you can simply static_assert that std::result_of<T()>::type is the same as void. However, in general, this is impossible because in C++ all callables (except degenerate ones like void()) have more than one possible call signature, thanks to features like implicit conversions.
† I might have to explain what I mean by "call signature" here. I mean the types used in an actual call, or potentially a call + assignment of return value, not the types that are explictly present in the declared signature. Consider the following code.
long f(double);
double d;
int i;
long a = f(d); // call signature is long(double)
// called with double, returning into a long
short b = f(i); // call signature is short(int)
// called with int, returning into a short
What you are seeing is a minor symptom of undefined behavior, or an error in the standard, or errors in compilers.
The general rule is that a std::function<A(B...)> does not enforce that the signature of passed function objects or pointers exactly match A(B...), but rather that they be compatible.
The standard describes this in terms of calling the incoming callable object with types B... and the result is implicitly convertible to A.
Now, the issue is that nothing is implicitly convertible to void. Arguably even void is not implicitly converted to void.
Some compilers take this clause, and interpret it to mean that std::function<void(B...)> can only be constructed from callables that return void when invoked with B... types. Others interpret it to mean that std::function<void(B...)> can be constructed from any callable that can be invoked with B... types.
I'm unsure what the standard says the language should do if the object you construct the std::function from violates the requirements dictated in the standard.
Now, to fix your problem, you can write this:
template<typename T, typename Sig>
struct invoke_return_value_matches;
template<typename T, typename R, typename... Args>
struct invoke_return_value_matches<T, R(Args...)>:
std::is_same< typename std::result_of<T(Args...)>::type, R >::type
{};
which is a trait that checks if the return value of invoking T with Args... matches R exactly.
If we want to create a quick wrapper for std::function that enforces exact match on return values:
template<typename Sig>
struct exact_function {
std::function<Sig> f;
template<typename...Args>
typename std::result_of< (Sig*)(Args...) >::type
operator()(Args&&...args) const {
return f(std::forward<Args>(args)...);
}
operator std::function<Sig>() const { return f; }
operator std::function<Sig>() && { return std::move(f); }
exact_function() = default;
exact_function(exact_function const&)=default;
exact_function(exact_function&&)=default;
exact_function& operator=(exact_function const&)=default;
exact_function& operator=(exact_function&&)=default;
template<
typename T,
typename=typename std::enable_if<invoke_return_type_matches<T,Sig>::value>::type
>
exact_function( T&& t ):f(std::forward<T>(t)) {}
exact_function( Sig* raw_func ):f(raw_func) {}
};
should be pretty close to what you want. I'm mainly just forwarding crap to the std::function internally, except I'm intercepting generic construct and applying your test.
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