问题
Here's the code I would like to work:
template <class T> void Foo(T&& param);
template <class F> void CallMe(F&& func)
{
func(42);
}
int main()
{
CallMe(Foo);
}
The compiler chokes when it tries to instantiate CallMe
because it doesn't know what I mean by Foo
. It works if I write Foo<int>
of course, but I'd like to avoid that because in the real code the template parameters can be complex.
My current workaround is to use callable objects instead of free functions. For example:
class Foo
{
public:
template <class T> void operator()(T&&);
};
This works fine, but I'm sure it will confuse users of my library. Is there some template magic I can use to make the first version work?
回答1:
Is there some template magic I can use to make the first version work?
I wish. And I hope someday. There have been several proposals in this area (e.g. P0119 and P0834). Until then, the best you can do is write a lifting macro to turn your name into a function object that calls that name:
#define FWD(...) std::forward<decltype(__VA_ARGS__)>(__VA_ARGS__)
#define LIFT(name) [&](auto&&... args) \
noexcept(noexcept(name(FWD(args)...))) \
-> decltype(name(FWD(args)...)) \
{ return name(FWD(args)...); }
This lets you write:
CallMe(LIFT(Foo));
which otherwise does what you want.
回答2:
It is possible to make something that somewhat resembles the code in the question, but not exactly.
A function call:
int main()
{
CallMe(Foo);
}
expects an object as a parameter. The object can be a function pointer, a class instance, a lambda, or something similar. It can't be an uninstantiated template function, since it is not an object. Only an instantiated function is an object.
As the original question states, using function-like objects is a work-around, and assumes that the following is too confusing for some users:
class Foo
{
public:
template <class T> void operator()(T&&);
};
Instead of that, it is possible to use a generic lambda, which is basically the same, but without all the boiler plate. A generic lambda looks almost like a regular function:
auto Foo = [](auto x)
{
std::cout << x << '\n';
};
int main()
{
CallMe(Foo);
}
回答3:
Is there some template magic I can use to make the first version work?
No.
The name of a function template don’t name a function type but a family of them. If it were possible, and the type of func
would be deduced from Foo
the type of T
will remain non-deduced which is needed in order to instantiate the Foo
.
回答4:
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
-> decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
#define OVERLOADS_OF(...) \
[](auto&&...args) \
RETURNS( __VA_ARGS__( decltype(args)(args)... ) )
then
CallMe(OVERLOADS_OF(Foo));
creates a function object that represents the overloads of the name Foo
, which include the functions produced by your template function named Foo
.
RETURNS
is also useful in other contexts. @Barry has a proposal to make RETURNS(X)
be similar to => X
, at least for lambdas.
来源:https://stackoverflow.com/questions/52675813/is-there-any-way-in-c-to-refer-to-a-function-template-while-neither-calling-it