#c
#c
Вопрос:
Как я могу определить шаблоны Add() ниже так, чтобы вызывались правильные на основе аргументов.
- Обе функции являются переменными.
- Add2 всегда имеет 2-й параметр, который является структурой базового BaseB.
struct BaseB {};
struct B1 : BaseB {};
struct B2 : BaseB {};
Add<Foo1>(a); // Call Add1
Add<Foo2>(a, "string"); // Call Add1
Add<Bar1>(a, B1()); // Call Add2
Add<Bar2>(a, B2(), "string"); // Call Add2
// Add1
template<typename R, typename A, typename... Params>
void Add(Aamp; a, Paramsamp;amp;... args) {
// ...
}
// Add2
template<typename R, typename A, typename B, typename... Params>
void Add(Aamp; a, Bamp; b, Paramsamp;amp;... args) {
// ...
}
Комментарии:
1. Это не компилируется. Это даже близко не подходит к компиляции. Поэтому невозможно понять, что вы пытаетесь сделать. Пожалуйста, не публикуйте код, который не компилируется.
Ответ №1:
Для C 17 вы можете использовать ответ @Jarod42 с перегруженной Add
функцией.
struct BaseB
{
};
struct B1 : BaseB {};
struct B2 : BaseB {};
template <typename R, typename A, typename ... Params>
void Add1(Aamp; a, Paramsamp;amp;... args)
{
std::cerr<<"Add1 called"<<std::endl;
}
template <typename R, typename A, typename B, typename ... Params>
void Add2(Aamp; a, Bamp;amp; b, Paramsamp;amp;... args)
{
std::cerr<<"Add2 called"<<std::endl;
}
template <typename R, typename A>
void Add(Aamp; a)
{
Add1<R>(a);
}
template<typename R, typename A, typename B, typename... Args>
void Add(Aamp; a, Bamp;amp; b, Argsamp;amp;... args) {
if constexpr (std::is_base_of<BaseB, std::decay_t<B>>::value) {
Add2<R>(a,std::forward<B>(b),std::forward<Args>(args)...);
} else {
Add1<R>(a,std::forward<B>(b),std::forward<Args>(args)...);
}
}
// try it
struct Foo1{};
struct Foo2{};
struct Bar1{};
struct Bar2{};
int main()
{
int a;
Add<Foo1>(a); // Call Add1
Add<Foo2>(a, "string"); // Call Add1
Add<Bar1>(a, B1()); // Call Add2
Add<Bar2>(a, B2(), "string"); // Call Add2
return 0;
}
Для C 14 это можно сделать с помощью SFINAE. Та же реализация также подходит для C 11, просто замените std::enable_if_t
и std::decay_t
соответствующими заменами.
struct BaseB
{
};
struct B1 : BaseB {};
struct B2 : BaseB {};
template <typename R, typename A, typename ... Params>
void Add1(Aamp; a, Paramsamp;amp;... args)
{
std::cerr<<"Add1 called"<<std::endl;
}
template <typename R, typename A, typename B, typename ... Params>
void Add2(Aamp; a, Bamp;amp; b, Paramsamp;amp;... args)
{
std::cerr<<"Add2 called"<<std::endl;
}
template <typename R, typename T1, typename T2, typename Enable=void>
struct AddImpl
{};
template <typename R, typename T1, typename T2>
struct AddImpl<R,T1,T2,
std::enable_if_t<!std::is_base_of<BaseB, std::decay_t<T2>>::value>
>
{
template <typename ... Args>
static void invoke(Argsamp;amp;... args)
{
Add1<R>(std::forward<Args>(args)...);
}
};
template <typename R, typename T1, typename T2>
struct AddImpl<R,T1,T2,
std::enable_if_t<std::is_base_of<BaseB, std::decay_t<T2>>::value>
>
{
template <typename ... Args>
static void invoke(Argsamp;amp;... args)
{
Add2<R>(std::forward<Args>(args)...);
}
};
template <typename R,typename A>
void Add(Aamp; a)
{
Add1<R>(a);
}
template <typename R, typename A, typename B, typename ... Args>
void Add(Aamp; a, Bamp;amp; b, Argsamp;amp;... args)
{
AddImpl<R,A,B>::invoke(a,std::forward<B>(b),std::forward<Args>(args)...);
}
// try it
struct Foo1{};
struct Foo2{};
struct Bar1{};
struct Bar2{};
int main()
{
int a;
Add<Foo1>(a); // Call Add1
Add<Foo2>(a, "string"); // Call Add1
Add<Bar1>(a, B1()); // Call Add2
Add<Bar2>(a, B2(), "string"); // Call Add2
return 0;
}
Печатает в обоих случаях:
Add1 called
Add1 called
Add2 called
Add2 called
ПРИМЕЧАНИЕ
Сигнатура функции Add(Aamp; a, Bamp; b, Paramsamp;amp;... args)
в вашем примере несовместима с вызовом Add<Bar1>(a, B1())
или Add<Bar2>(a, B2(), "string")
потому, что вы не можете передать временную переменную в качестве ссылки lvalue для аргумента. Итак, в приведенных выше примерах Bamp;amp; b
вместо этого используется. Если вам нужно Bamp; b
, вы должны изменить тест так, чтобы использовались ссылки lvalue.
Ответ №2:
С C 17 вы можете сделать
template<typename R, typename A, typename B, typename... Params>
void Add(Aamp; a, Bamp;amp; b, Paramsamp;amp;... args) {
if constexpr (std::is_base_of<BaseB, std::decay_t<B>>::value) {
// Add2's code
} else {
// Add1's code
}
}
// To handle empty pack
template <typename R, typename A>
void Add(Aamp; a)
{
// Add1's code
}
Прежде чем вы могли бы использовать диспетчеризацию SFINAE / tag.
Отображение диспетчеризации тегов:
template<typename R, typename A, typename... Params>
void AddImpl(std::false_type, Aamp; a, Paramsamp;amp;... args) {
// Add1's code
}
template<typename R, typename A, typename B, typename... Params>
void AddImpl(std::true_type, Aamp; a, Bamp;amp; b, Paramsamp;amp;... args) {
// Add2's code
}
// To handle empty pack
template <typename R, typename A>
void Add(Aamp; a)
{
AddImpl<R>(std::false_type{}, a); // Add1's code
}
template<typename R, typename A, typename B, typename... Params>
void Add(Aamp; a, Bamp;amp; b, Paramsamp;amp;... args) {
AddImpl<R>(std::is_base_of<BaseB, std::decay_t<B>>{},
a,
std::forward<B>(b),
std::forward<Params>(ars)...);
}
Комментарии:
1. Хорошая попытка, но эта не может справиться
Add<Foo1>(a)
2. Действительно, это можно легко исправить с помощью дополнительной перегрузки.