当可变参数模板类从模板参数继承时,在调用基本类型的方法时扩展参数包

时间:2018-12-28 15:41:13

标签: c++ c++11 variadic-templates

因此,基本上我想定义一个从任意数量的类继承的类,并在其中定义一个从所有基类中调用重载方法的方法。

我尝试编写此代码,但无法编译:

class Foo
{
public:
    void method()
    {
        std::cout << "Foo::method()\n";
    }
};

class Bar
{
public:
    void method()
    {
        std::cout << "Bar::method()\n";
    }
};

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts): Ts(ts)... {}
    Combined(Ts&&... ts): Ts(std::move(ts))... {}

    template <typename U>
    void call_methods()
    {
        U::method();
    }

    template <typename U, typename... Us>
    void call_methods()
    {
        U::method();
        call_methods<Us...>();
    }

    void method()
    {
        call_methods<Ts...>();
    }
};


int main(int argc, char *argv[])
{
    Combined<Foo, Bar> obj({}, {});
    obj.method();
    return 0;
}

编译器说以下内容:

test.cpp:42:9: error: call to member function 'call_methods' is ambiguous
        call_methods<Us...>();
        ^~~~~~~~~~~~~~~~~~~
test.cpp:47:9: note: in instantiation of function template specialization
      'Combined<Foo, Bar>::call_methods<Foo, Bar>' requested here
        call_methods<Ts...>();
        ^
test.cpp:57:9: note: in instantiation of member function
      'Combined<Foo, Bar>::method' requested here
    obj.method();
        ^
test.cpp:33:10: note: candidate function [with U = Bar]
    void call_methods()
         ^
test.cpp:39:10: note: candidate function [with U = Bar, Us = <>]
    void call_methods()
         ^

基本上,call_methods<U = Bar>call_methods<U = Bar, Us = <>>之间存在歧义。但是,如果我声明了void call_methods() {},则由于某种原因它将与call_methods<Us...>();不匹配。

如果尚不清楚,我希望Combined<Foo, Bar>::method()呼叫Foo::method()Bar::method()

我知道我可以通过将具有相应类型的对象的tuple作为成员并对其进行迭代来实现此目的,但是我真的很想找到一个与我编写的内容更接近的解决方案。 / p>

4 个答案:

答案 0 :(得分:1)

对此问题有多种解决方案。最简单的方法是在适当位置扩展调用,而不是递归扩展。遵循以下内容:

struct Foo
{
    void method();
};

struct Bar
{
    void method();
};

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts);
    Combined(Ts&&... ts);

    void method()
    {

        bool z[] = { (Ts::method(), true)... };
        (void)z;
    }
};


int main(int argc, char *argv[])
{
    Combined<Foo, Bar> obj({}, {});
    obj.method();
    return 0;
}

答案 1 :(得分:1)

解决错误的原因,发生这种情况是因为重载分辨率与功能参数有关,而与功能模板参数无关。

实例化foo<Bar>()与重载解析(一个参数带有一个参数或一个空参数包带有一个参数?)是无法区分的,因此它最终在调用中含糊不清。

SergeyA's answer中所述,解决此问题的一种方法是仅具有一个重载并进行in-site扩展调用。

答案 2 :(得分:1)

要解决您的问题,请在参数包为空时禁用第二个重载:

template <typename U, typename... Us>
typename std::enable_if< (sizeof...(Us) > 0) >::type
call_methods()
{
    U::method();
    call_methods<Us...>();
}

摆脱歧义。

Live example.

答案 3 :(得分:0)

一种解决方案:

template <typename... Ts>
class Combined: public Ts...
{
public:
    Combined(const Ts&... ts): Ts(ts)... {}
    Combined(Ts&&... ts): Ts(std::move(ts))... {}

    void method()
    {
        int dummy[] { (Ts::method(), 0)... };
    }
};

不理想,但应该和我最初的想法一样有效。