I have a template<typename T> class Foo
.
I want to declare a function that can return any kind of Foo. I would do it like this: template<typename T> Foo<T> bar();
But this means that I have to use it like this: Foo<SomeConcreteT> f = bar<SomeConcreteT>();
, and SomeConcreteT
can be very long and clunky and annoying to have to type out.
I do, however, have some
using AppleFoo = Foo<ABunchOfStuffForApples>;
using BananaFoo = Foo<SomethingElseForBananas>;
// ...
I would prefer to call bar like this: AppleFoo f = bar<AppleFoo>();
, so I don't have to type out ABunchOfStuffForApples
all the time, and conceptually, AppleFoo
telly the reader more about what's supposed to happen here than ABunchOfStuffForApples
does.
I can do it by adding using TType = T
inside Foo
and a helper function like this:
template<typename F>
F bar()
{
return bar<F::TType>();
}
But this is ugly and error-prone (e.g. calling bar<SomethingThatIsNotAFoo>()
).
Is there a cleaner way of doing this?
And is there, more generally, a way of testing whether some type is a SomethingKnown<SomethingUnknown>
, e.g. in a static_assert
?
CodePudding user response:
You can pretend to have return-type deduction, by having bar
be a non-template that returns a proxy with a operator Foo<T>
template <typename T>
struct Foo {
/* ... */
};
namespace detail {
struct bar_t {
template <typename T>
operator Foo<T>() { /* current implemenation of bar */ }
};
}
detail::bar_t bar() { return {}; }
using AppleFoo = Foo<struct Apple>;
int main() {
AppleFoo f = bar();
}
CodePudding user response:
You can create a traits to know if it is a Foo and extract its template parameter:
template <typename T>
struct is_foo : std::false_type {};
template <typename T>
struct is_foo<Foo<T>> : std::true_type
{
using type = T;
};
and then
template<typename FOO>
FOO bar()
{
static_assert(is_foo<FOO>::value);
using T = typename is_foo<FOO>::type;
// ...
}
CodePudding user response:
As alternative, if you can change calling syntax, you might pass a tag, something along
template <typename T> struct tag {};
and then
template<typename T>
Foo<T> bar(tag<Foo<T>>)
{
return bar<T>();
}
with usage similar to
using AppleFoo = Foo<ABunchOfStuffForApples>;
auto bar1 = bar(tag<AppleFoo>{});
auto bar2 = bar(tag<Foo<ABunchOfStuffForApples>>{});
// auto is AppleFoo and so Foo<ABunchOfStuffForApples>
If you dislike tag in interface, you can still use it as implementation:
template<typename T>
Foo<T> bar_impl(tag<Foo<T>>)
{
// implementation, such as return Foo<T>();
}
// Possibly other specialization for Foo<std::vector<T>>, Foo<int>, ..
template<typename T>
auto bar()
{
return bar_impl(tag<T>{});
}
with usage
using AppleFoo = Foo<ABunchOfStuffForApples>;
auto bar1 = bar<AppleFoo>();
auto bar2 = bar<Foo<ABunchOfStuffForApples>>();
// auto is AppleFoo and so Foo<ABunchOfStuffForApples>