I want to deduce the return type of a function coming as a template parameter. Consider the following code:
#include <type_traits>
struct Result {};
Result foo() { return Result{}; }
template<typename Factory>
void check(Factory) {
using ActualResult = typename std::result_of<Factory()>::type;
static_assert(std::is_same<Result, ActualResult>::value, "");
}
int main() {
check(foo);
}
This works as expected. However, if I change the parameter of check() to const Factory&, then it does not compile. The error from gcc is:
prog.cc: In instantiation of 'void check(const Factory&) [with Factory = Result()]':
prog.cc:14:14: required from here
prog.cc:9:66: error: function returning a function
using ActualResult = typename std::result_of<Factory()>::type;
^
prog.cc:10:65: error: function returning a function
static_assert(std::is_same<Result, ActualResult>::value, "");
^
What's the problem here? How can I make it work?
Functions (just like arrays) can neither be passed as prvalue arguments or returned as prvalues.
Therefore, template <typename Factory> void check(Factory), which takes a prvalue argument, will cause foo to decay to the function pointer, and check(foo) will cause Factory to be deduced as Result (*)(). Finally, result_of<Factory()> gives the result of calling the callable type that is the function pointer with no arguments.
When you change check to check(const Factory&), the function takes an lvalue, and so there is no decay, and Factory is deduced as the function type Result(). This is not a type that you are allowed to pass to result_of*, which requires either a callable type or a reference to a function. That is, you should use result_of<Factory&()> in that case.
*) In C++11. The rules for result_of may have been relaxed in later revisions, and C++17 deprecates result_of.
Related
The following code compiles with c++11 or higher
#include <iostream>
#include <cstdlib>
#include <cstdio>
#define p 1
// p1: prototype 1
template <class Function, class... Args>
void addv(Function&& f, Args&... args) {
std::cout << f(args...) << std::endl;
}
// p2: prototype 2
template <class Function, class... Args>
void addv(Function& f, Args&... args) {
std::cout << f(args...) << std::endl;
}
int add(int& a, int& b) {
return a+b;
}
class Adder {
public:
int operator () (int& a, int&b) {
return a+b;
}
};
int main() {
int a = 2;
int b = 1;
Adder adder;
addv<int (int&,int&),int,int>(add,a,b); // uses p1 OR p2
addv<Adder,int,int>(Adder(),a,b); // uses p1
addv<Adder,int,int>(adder,a,b); // uses p2
}
If prototype 2 is removed, and this is compiled, the following error happens:
exp.cpp:36:36: error: no matching function for call to ‘addv<Adder, int, int>(Adder&, int&, int&)’
addv<Adder,int,int>(adder,a,b); // uses p2
^
exp.cpp:9:10: note: candidate: template<class Function, class ... Args> void addv(Function&&, Args& ...)
void addv(Function&& f, Args&... args) {
^~~~
exp.cpp:9:10: note: template argument deduction/substitution failed:
exp.cpp:36:36: note: cannot convert ‘adder’ (type ‘Adder’) to type ‘Adder&&’
addv<Adder,int,int>(adder,a,b); // uses p2
^
Why adder cannot be converted from a lvalue to a rvalue implicitly as is needed to have the line addv<Adder,int,int>(adder,a,b); use prototype 1?
Is it possible to explicitly create a rvalue reference of adder to have it correctly match prototype 1?
It can't be converted because the idea with using rvalue and lvalue references in function signatures is exactly to make sure you get one or the other. Not either one.
Normally that is used because if you get an rvalue, you can move it. If you get an lvalue you need to copy it. You can also make sure that a function is only callable with an rvalue, or lvalue.
When passing functions the usual way is to take the parameter by value. That also works with both rvalues and lvalues. This is how it's (almost always?) done in the standard library. Function pointers and Functors are generally very cheap to copy.
If you want a signature that can take both rvalues and lvalues you can use const &.
Also note that since Function&& is a template parameter, it is a forwarding reference. Thats means it will become an rvalue reference or lvalue reference depending on what you pass in.
When you call the function with an explicitly specified parameter though
addv<Adder,int,int>(adder,a,b);
^--this
the template parameter will be deduced to exactly Adder, and your function will then accept only rvalues, since the signature says Function&& -> Adder&&.
The easy way to make the code work is to not explicitly specify the template parameter.
addv(adder,a,b);
Then you can remove prototype 2 and all the function calls will work.
If you really want to or need to specify the parameters, you can use std::move to convert an lvalue to an rvalue at the calling site.
addv<Adder,int,int>(std::move(adder),a,b);
Edit: Convert might be a bit misleading. It's actually a cast. Nothing is changed except the value category.
Your code will compile if you don't explicitly specify template parameters and let template type deduction work it out:
addv(Adder(),a,b); // this is deduced to
// addv<Adder, int, int>(Adder&&, int&, int&)
addv(adder,a,b); // this is deduced to
// addv<Adder&, int, int>(Adder& &&, int&, int&),
// which becomes addv<Adder&, int,int>(Adder&, int&, int&)
// after reference collapsing
So you do want to explicitly specify template parameter, it needs to be
addv<Adder&, int,int>(adder,a,b);
Have a look at Scott Meyers' excellent article on universal reference (and in particular 'reference collapsing'), hope it helps.
I have the following piece of code:
#include <functional>
struct X {
int get() const& {
return 42;
}
};
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
X x;
return fn(x);
}
int main(void) {
Apply([](X const& x){return x.get();});
//Apply(std::mem_fn(&X::get)); // does not compile
}
The first call to Apply compiles fine, but if I uncomment the second call, I get the following compilation error:
main.cpp:16:5: error: no matching function for call to 'Apply'
Apply(std::mem_fn(&X::get)); // does not compile
^~~~~
main.cpp:10:27: note: candidate template ignored: substitution failure [with Func = std::_Mem_fn<int (X::*)() const &>]: no type named 'type' in 'std::result_of<std::_Mem_fn<int (X::*)() const &> (X)>'
std::result_of_t<Func(X)> Apply(Func fn) {
^
I somehow expected that both calls could be used interchangeably and that std::mem_fn just "would do the right thing". Can anybody explain, what happens here?
The problem is here:
int get() const& {
// ^^^
Your member function is lvalue-reference qualified. In your Apply():
template<typename Func>
std::result_of_t<Func(X)> Apply(Func fn) {
return fn(X{});
}
you're invoking it with an rvalue. Which brings us to the [very surprising to me] difference between these two expressions:
X{}.get(); // ok
(X{}.*&X::get)(); // ill-formed
On specifically pointer-to-member operators, the ref-qualifiers of the member pointer are checked against the value category of the object. From [expr.mptr.oper]:
In a .* expression whose object expression is an rvalue, the program is ill-formed if the second operand is a pointer to member function with ref-qualifier &. In a .* expression whose object expression is an lvalue, the
program is ill-formed if the second operand is a pointer to member function with ref-qualifier &&.
So the first expression is okay, get() is const&-qualified but rvalues can bind to that. The second expression is not okay - the rules just explicitly prohibit it.
So the behavior you see is perfectly correct - mem_fn is defined by directly invoking the member function, which is ill-formed on an rvalue, so Apply is removed from the overload set. If it were not, then instantiating the body would be a hard error.
The reason the lambda works is that the temporary X is bound to the lambda's reference parameter. get() is then invoked on the lvalue function parameter - not on the temporary passed into it. But even without that, invoking get() directly on the temporary would still be fine.
#include <iostream>
template <typename T>
class test
{
public:
test(T&& t)
{
}
};
template <typename T>
void succeed(T&& t)
{
}
int main()
{
int i = 1;
test<int> t(i); // failed to compile
succeed(i); // OK
return 0;
}
Error from GCC 5.2:
main.cpp: In function 'int main()':
main.cpp:20:18: error: cannot bind 'int' lvalue to 'int&&'
test t(i);
^
main.cpp:7:5: note: initializing argument 1 of 'test::test(T&&) [with T = int]'
test(T&& t)
^~~~
Could someone explain why the class template cannot compile but function template is OK?
Thanks.
In succeed, T&& t is a forwarding reference, not an rvalue reference. But in test, it is an rvalue reference.
A forwarding reference happens only when the parameter is T&&, and T is a template parameter of that function. In your code T is a template parameter of the enclosing class, so it doesn't count as a forwarding reference.
A forwarding reference may bind to both lvalues and rvalues.
During the drafting of C++11 it was suggested to use different syntax for forwarding references than rvalue references (instead of using T&& t for both); however the committee eventually settled on the current behaviour.
For a more detailed description of template parameter deduction, including a more precise specification of when T&& becomes a forwarding reference, see here -- search for the term "forwarding reference" to find the special rules for forwarding references.
Your confusion is probably rooted in your assumption that in both cases T is int. This is why you presume that these two cases as similar. In reality they are not.
In the class version you are manually specifying what T is. You explicitly tell the compiler that T is int. Constructor parameter type T && in this case becomes int &&, which cannot bind to a regular lvalue. Hence the error.
In the function version you don't tell the compiler what T is, but instead you expect the compiler to deduce it. In situations like yours the language is deliberately designed to deduce T as int & (note: not as int, but rather as int &). Once T is deduced as int &, the so called "reference collapsing" rules lead to function parameter type T && becoming int & - an ordinary lvalue reference. This parameter can successfully bind to lvalue argument i.
That explains the difference you observe.
For the sake of experiment, in the latter case you can suppress template argument deduction and specify the template argument explicitly
succeed<int>(i);
That will forcefully specify T as int and lead to the very same error as in the class version for the very same reason.
Similarly, you can "simulate" function's behavior for your class by specifying the template argument as int &
test<int &> t(i);
The same "reference collapsing" rules will make your constructor invocation to compile successfully.
I am writing a library with many function objects whose classes have several operator() overloads that do not depend on the state of the classes and do not alter it. Now, I tried to make my code work with many old-style APIs (it is not a random need, I actually had to deal with such APIs) and therefore decided to make the function objects convertible to any function pointer corresponding to one of the overloads. At some point, I realized that I had too many such conversions to function pointer operators and that I should theorically be able to write a single variadic conversion operator. Here is a class implementing such a variadic operator:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator fptr_t<Args...>() const
{
return [](Args... args) {
// Whatever
};
}
};
As you can see, I used the lambda conversion to function pointer to implement the conversion operator, which is not a problem since every function object I have is stateless. The goal was to be able to use the class as follows:
int main()
{
void(*foo)(int) = foobar();
void(*bar)(float, double) = foobar();
}
g++ has no problem compiling this code with the expected semantics. However, clang++ rejects it with a template substitution failure error:
main.cpp:21:11: error: no viable conversion from 'foobar' to 'void (*)(int)'
void(*foo)(int) = foobar();
^ ~~~~~~~~
main.cpp:11:5: note: candidate function [with Args = int]
operator fptr_t<Args...>() const
^
1 error generated.
Note that clang++ has no problem with such conversion operators as long as no variadic templates are involved. If I use a single template parameter, it will have no problem compiling the code. Now, should the code above be accepted or rejected by the compiler?
A lambda can only be converted to a function pointer if it does not capture, so your code should work. This is justified in the standard 5.1.2/p6 Lambda expressions [expr.prim.lambda] (Emphasis Mine):
The closure type for a non-generic lambda-expression with no
lambda-capture has a public non-virtual non-explicit const conversion
function to pointer to function with C++ language linkage (7.5) having
the same parameter and return types as the closure type’s function
call operator. The value returned by this conversion function shall be the
address of a function that, when invoked, has the same effect as invoking the
closure type’s function call operator.
So I would file it as a CLANG bug.
As a work around for CLANG, you can convert it to a std::function as shown below:
struct foobar
{
template<typename... Args>
using fptr_t = void(*)(Args... args);
template<typename... Args>
operator std::function<void(Args...)>() const
{
return [](Args... args) {
//...
};
}
};
int main()
{
std::function<void(int)> f1 = foobar();
std::function<void(double, float)> f2 = foobar();
f1(1);
f2(2.0, 1.0f);
}
Live Demo
When I run this code with libc++:
struct foo
{
foo(int x) : x(x)
{}
int x;
};
int main()
{
const auto select_x = std::mem_fn(&foo::x);
foo f(1);
printf("%i\n", select_x(f));
}
I get an error like this:
mem_fn.cpp:16:20: error: no matching function for call to object of type
'const std::__1::__mem_fn<int foo::*>'
printf("%i\n", select_x(f));
^~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/functional:1224:11: note:
candidate function not viable: 'this' argument has type 'const
std::__1::__mem_fn<int foo::*>', but method is not marked const
operator() (_ArgTypes&&... __args)
^
It seems that libc++ is missing a const overload. It work using libstdc++. Is this a bug in libc++? Or does the standard require a const overload?
I don't see the standard specifying anything about the const-callability of the wrapper returned by mem_fn. From [func.memfn] (quoting N4140):
template<class R, class T> unspecified mem_fn(R T::* pm);
1 Returns: A simple call wrapper (20.9.1) fn such that the
expression fn(t, a2, ..., aN) is equivalent to INVOKE (pm, t, a2, ..., aN)
(20.9.2). fn shall have a nested type result_type that
is a synonym for the return type of pm when pm is a pointer to
member function.
2 The simple call wrapper shall define two nested types named
argument_type and result_type as synonyms for cv T* and Ret,
respectively, when pm is a pointer to member function with
cv-qualifier cv and taking no arguments, where Ret is pm’s
return type.
3 The simple call wrapper shall define three nested types
named first_argument_type, second_argument_type, and
result_type as synonyms for cv T*, T1, and Ret, respectively,
when pm is a pointer to member function with cv-qualifier cv and
taking one argument of type T1, where Ret is pm’s return type.
4 Throws: Nothing.
The reference to [func.def] (though simple call wrapper is actually defined in [func.require]) is equally unilluminating. This pretty much only guarantees that whatever it returns is usable directly.
As a quality of implementation issue, though, I see no reason why the wrapper shouldn't be const-callable, and it looks like this actually has been fixed over a year ago. You might want to try updating your XCode to see if you can pick up a more recent version of the library.