开发者

Passing variadic class template's sub-classes to function that only accepts the base class (via parameter pack deduction/inference)

**I've gotten a few suggestions to make my function pure generic, which would work, but I'd prefer limiting the function to only accept Base and its children.

Having trouble making a function that can accept arguments of a variadic template class base type, while the function will actually be called with classes that derive from Base. I've tried a few things. Here's the general idea. Given:

template<typename... Args> struct Base {
    std::tuple<Args...> data;
    ... //other stuff
};

struct DerivedA : Base<string, int> {
};

struct DerviedB : Base<bool, string, int> {
};

What's the correct way to create a function that does this:

string moosh_together(Base A, Base B) { //I only need access to Base's members
    return get<0>(A.data) + get<1>(B.data);
}

main() {
    DerivedA aThing;
        get<0>(aThing.data) = "foo";
    DerivedB bThing;
        get<1>(bThing.data) = "bar'd";
    cout << moosh_together(aThing, bThing) &开发者_如何转开发lt;< endl;
}

Output:

foobar'd

I've tried a few variations of the moosh_together function, none of which work. Leaving it as above generates a compiler error about missing template arguments. I'm unsure how to pass through to the function the template arguments that define DerivedA and DerivedB.

Other things I've tried (shotgun method):

string moosh_together(Base<> A, Base<> B) {}
//err: conversion from 'DerivedA' to non-scalar type 'Base<>' requested

template<Base<typename... Args> T1, Base<typename... Args> T2>
string moosh_together(T1 A, T2 B) {}
//err: expected paramter pack before '...'

template<Base<Args...> T1, Base<Args...> T2>
string moosh_together(T1 A, T2 B) {}
//err: 'Args' was not declared in this scope


Edit:

If you need both parameter packs, you can just put both in the template specification:

template<typename... ArgsA, typename... ArgsB>
string moosh_together(const Base<ArgsA...>& A, const Base<ArgsB...>& B) {
    return get<0>(A.data) + get<1>(B.data);
}

This works because the parameter packs are inferred from arguments and not specified in a list. Naturally you can't have a class that depends on multiple parameter packs.


When you write: string moosh_together(Base A, Base B), ask yourself what Base is. Base is a class template, not a class type.

In other words, given:

template <typename T>
struct foo {};

foo<int> and foo<float> are two totally different types, that just so happened to be made from the same class template. They have no common base class, and you cannot refer to them simply as foo anymore than you can refer to both int and float with a single type.

You could factor out the non-dependent parts of Base:

struct Core
{
    string name;
};

template <typename... Args>
struct Base : Core
{
    // ...
};

And then refer to the Core portions:

// pass by reference, to avoid unnecessary copying
string moosh_together(const Core& a, const Core& b);

Or just make the function totally generic:

template <typename BaseOne, typename BaseTwo>
string moosh_together(const BaseOne& a, const BaseTwo& b);

And say "if you have the necessary members, you can use this function".


Couldn't you create a base class in the inheritance hierarchy for Base and pass that to the moosh_together() function? (Low knowledge of c++ here)


I think the general expansion of this is

string moosh_together(Base<T1...> A1, Base<T2...> A2, ... Base<Tn...> An) {
    return get<0>(A1.data) + get<1>(A2) + ... + get<n-1>(An.data);
}

This could be written as follows

template<int I> 
string moosh_together() { return ""; }

template<int I, typename ...Base1Ty, typename ... Bases>
string moosh_together(Base<Base1Ty...> const& base1, Bases const&... bases) {
    return get<I>(base1.data) + moosh_together<I+1>(bases...); 
}

template<typename ... Bases>
string moosh_together(Bases const&... bases) {
    return moosh_together<0>(bases...);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜