当可变参数模板 class 从模板参数继承时,在调用基本类型的方法时扩展参数包
Expand parameter pack in calling the base types' methods when variadic template class is inherited from template arguments
所以基本上我想定义一个 class 继承自任意数量的 classes 并且其中有一个方法可以从所有基础 class 调用重载方法es.
我试过写这个,但编译不了:
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
将相应类型的对象作为成员并迭代它们来实现这个,但我真的想找到一个更接近我写的解决方案.
这个问题有多种解决方案。最简单的方法是就地扩展调用,而不是递归地扩展。以下几行内容:
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;
}
解决方案之一:
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)... };
}
};
不理想,但应该和我最初的想法一样有效。
解决错误的原因,发生这种情况是因为重载解析与函数参数有关,而不是函数模板参数。
实例化foo<Bar>()
与重载决议没有区别(一个参数的那个还是一个空参数包的?),因此它以一个通话中出现歧义。
如 中所述,解决此问题的一种方法是只进行一次重载并 in-site
扩展调用。
要修复您的解决方案,请在参数包为空时禁用第二个重载:
template <typename U, typename... Us>
typename std::enable_if< (sizeof...(Us) > 0) >::type
call_methods()
{
U::method();
call_methods<Us...>();
}
消除歧义。
所以基本上我想定义一个 class 继承自任意数量的 classes 并且其中有一个方法可以从所有基础 class 调用重载方法es.
我试过写这个,但编译不了:
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
将相应类型的对象作为成员并迭代它们来实现这个,但我真的想找到一个更接近我写的解决方案.
这个问题有多种解决方案。最简单的方法是就地扩展调用,而不是递归地扩展。以下几行内容:
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;
}
解决方案之一:
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)... };
}
};
不理想,但应该和我最初的想法一样有效。
解决错误的原因,发生这种情况是因为重载解析与函数参数有关,而不是函数模板参数。
实例化foo<Bar>()
与重载决议没有区别(一个参数的那个还是一个空参数包的?),因此它以一个通话中出现歧义。
如 in-site
扩展调用。
要修复您的解决方案,请在参数包为空时禁用第二个重载:
template <typename U, typename... Us>
typename std::enable_if< (sizeof...(Us) > 0) >::type
call_methods()
{
U::method();
call_methods<Us...>();
}
消除歧义。