Home > Blockchain >  Declare template<typename Foo<T>> instead of template<typename T>
Declare template<typename Foo<T>> instead of template<typename T>

Time:02-14

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>
  • Related