3

So, I was working on an solution on how to solve the problem in Looking for design pattern to reduce virtual method overloads

My idea was too use variadic templates to describe which types may be accepted by a specific class. This might have a small use case, but I like to play around with templates...

Here is what I came up with (so far):

struct Struct
{
    virtual void doit() = 0;
};

struct StructA : Struct
{
    void doit() { std::cout << "A" << std::endl; }
};

struct StructB : Struct
{
    void doit() { std::cout << "B" << std::endl; }
};

struct StructC : Struct
{
    void doit() { std::cout << "C" << std::endl; }
};

template <typename Type>
struct Accepter
{
    void accept(const Type& t) { t.doit(); };
};

template <typename...Types>
struct MultiAccepter : Accepter<Types>... {};

When passing only a single type to MultiAccepter everything does as it should. The problems only occur as soon as I pass 2 or more template parameter types. It looks like the compiler looses its ability to differ between distinct types.

int main()
{
    StructA struct_a;
    StructB struct_b;

    Accepter<StructA>                           accept_a;
    Accepter<StructB>                           accept_b;
    MultiAccepter<StructA>                      accept_multi_a;
    MultiAccepter<StructB>                      accept_multi_b;
    MultiAccepter<StructA, StructB>             accept_multi_ab;



    accept_a.accept(struct_a);              //OK
    accept_b.accept(struct_b);              //OK
    accept_multi_a.accept(struct_a);        //OK
    accept_multi_b.accept(struct_b);        //OK
    accept_multi_ab.accept(struct_a);       //NOK:
                                            //  error C2385: ambiguous access of 'accept'
                                            //      note : could be the 'accept' in base 'Accepter<StructA>'
                                            //      note : or could be the 'accept' in base 'Accepter<StructB>'
    accept_multi_ab.accept(struct_b);       //NOK:
                                            //  error C2385: ambiguous access of 'accept'
                                            //      note : could be the 'accept' in base 'Accepter<StructA>'
                                            //      note : or could be the 'accept' in base 'Accepter<StructB>'
                                            //  error C2664 : 'void Accepter<StructA>::accept(const Type &)' : cannot convert argument 1 from 'StructB' to 'const StructA &'
                                            //      with
                                            //      [
                                            //          Type = StructA
                                            //      ]
                                            //      note : Reason : cannot convert from 'StructB' to 'const StructA'
                                            //      note : No user - defined - conversion operator available that can perform this conversion, or the operator cannot be called
    return 0;
}

Trying to compile the code with gcc 5.2 doesn't work either: http://goo.gl/oVLHT8

I guess it's just a simple problem, but I just can't find a solution for it. Does somebody know what I'm doing wrong?


P.S.: The example on this side describes a similar pattern: http://natsys-lab.blogspot.de/2013/07/c-variadic-templates-and-multiple.html


Update: It looks like the base class Accepter<StructB> defines a function void accept(const StructA&). I still don't get why this is the case.

Community
  • 1
  • 1
Simon Kraemer
  • 5,700
  • 1
  • 19
  • 49
  • 2
    This is a dupe of...something. Without a using-declaration, names from different base class scopes don't overload. – T.C. Nov 09 '15 at 18:07
  • @T.C. Right. It needs a variadic using directive, which I don't know how to do. Also note http://stackoverflow.com/questions/7870498/using-declaration-in-variadic-template – Johan Lundberg Nov 09 '15 at 18:11
  • 2
    @T.C. [This](http://stackoverflow.com/q/28348429/2069064)? Even has the same comment from you – Barry Nov 09 '15 at 18:16

1 Answers1

3

I put togheter a solution using the pattern described here: https://stackoverflow.com/a/28349054/1149664

The trick is to inherit one base class Accepter<T0> at a time, and then push the remaining types further up the inheritance chain. I also made the methods const to get it consistent. You can tweak that as you like depending on what you need.

#include "stdafx.h"
#include <functional>
#include <chrono>
#include <iostream>


struct Struct
{
    virtual void doit() const = 0;
};

struct StructA : public Struct
{
    void doit() const { std::cout << "A" << std::endl; }
};

struct StructB : public Struct
{
    void doit() const { std::cout << "B" << std::endl; }
};

struct StructC : public Struct
{
    void doit() const { std::cout << "C" << std::endl; }
};

template <typename Type>
struct Accepter
{
    void accept(const Type& t) const { t.doit(); } ;
};

template <typename... Types>
struct MultiAccepter;

template <typename T0, typename...Types>
struct MultiAccepter<T0, Types...> : public Accepter<T0>, public MultiAccepter<Types...> {
    using Accepter<T0>::accept;
    using MultiAccepter<Types...>::accept;
};

template <typename T0>
struct MultiAccepter<T0> : public Accepter<T0> {
    using Accepter<T0>::accept;
};

int main()
{
    StructA struct_a;
    StructB struct_b;

    Accepter<StructA>                           accept_a;
    Accepter<StructB>                           accept_b;
    MultiAccepter<StructA>                      accept_multi_a;
    MultiAccepter<StructB>                      accept_multi_b;
    MultiAccepter<StructA, StructB>             accept_multi_ab;

    accept_a.accept(struct_a);              
    accept_b.accept(struct_b);             
    accept_multi_a.accept(struct_a);       
    accept_multi_b.accept(struct_b);     
    accept_multi_ab.accept(struct_a);    
    accept_multi_ab.accept(struct_b);     
    return 0;
}
Community
  • 1
  • 1
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97