方法,该方法调用所有基类的同名方法(如果存在),并将返回值保存到列表中

method, that calls same-named method of all base classes, if exists and saves return value to a list

本文关键字:方法 返回值 保存 列表 存在 调用 基类 如果      更新时间:2023-10-16

我正在编写一个宏,它生成助手方法来调用当前类的所有基类的所有同名方法,并跳过没有该名称方法的基类。(我没有发布实际的宏,因为我不想伤害任何人,下面是我的一个宏生成方法的测试文件)

我设法使它在不保留这些方法的返回值的情况下工作。现在我想保存这些值并返回一个列表。

下面是一个函数,由我的宏生成,它应该调用所有基的名为"base_method"的方法,以int和string作为参数。

我不明白,为什么我会得到错误(在代码下面)。

#include <type_traits>
#include <list>
#include <iostream>
namespace detail{
  template <typename> struct sfinae_true : std::true_type{};
}
namespace detail{ 
    template <typename T, typename A1, typename A2>
    static auto test_base_method(int) -> 
        sfinae_true<decltype(std::declval<T>().base_method(std::declval<A1>(), std::declval<A2>()))>; 
    template <typename , typename A1, typename A2>
    static auto test_base_method(long) -> 
        std::false_type; 
    template <typename T, typename A1, typename A2>
    struct has_base_method : decltype(test_base_method<T, A1, A2>(0)){}; 
    template <typename Base, typename T, std::enable_if_t<has_base_method<Base,int,std::string>::value, bool> = true >
    auto call_base_method_if_any(T& obj, int arg1, std::string arg2) -> 
        decltype( obj.Base::base_method(std::declval<int>(), std::declval<std::string>()))
    { 
        return obj.Base::base_method(arg1, arg2); 
    } 
    template <typename Base, typename T, std::enable_if_t<!has_base_method<Base,int,std::string>::value, bool> = false>
    auto call_base_method_if_any(T&, int, std::string) -> bool
    { 
        return false; 
    } 
};
template <typename ... T>
class Z : public T ... {
public: 
    auto call_base_method_of_all_bases_if_any(int arg1, std::string arg2) -> std::list<bool> { 
        return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
    }
};
struct A{
    bool base_method(int, bool){ std::cout << "An"; return true; }
    bool base_method_narg(){ std::cout << "A no argn"; return true; }
};
struct B{ void base_method(int, bool){ std::cout << "Bn"; } };
struct C{ void base_method(int a, std::string b){ std::cout << "C, int = " << a << ", string = " << b; } };
struct D{ };
int main(){
    Z<A> b;
    Z<A,B> c;
    Z<A,B,C> d;
    Z<A,B,C,D> a;
    std::cout << "a:" << std::endl;
    auto x =a.call_base_method_of_all_bases_if_any(0, "string");
    std::cout << std::endl;
    std::cout << "b:" << std::endl;
    b.call_base_method_of_all_bases_if_any(0, "string");
    std::cout << std::endl;
    std::cout << "c:" << std::endl;
    c.call_base_method_of_all_bases_if_any(0, "string");
    std::cout << std::endl;
    std::cout << "d:" << std::endl;
    d.call_base_method_of_all_bases_if_any(0, "string");
    std::cout << std::endl;
}

编译错误:

     g++ --std=c++14 expression_sfinae.3.cpp                                                                                                                         
    expression_sfinae.3.cpp: In instantiation of ‘std::__cxx11::list<bool> Z<T>::call_base_method_of_all_bases_if_any(int, std::__cxx11::string) [with T = {A, B, C, D}; std::__cxx11::string = std::__cxx11::basic_string<char>]’:
    expression_sfinae.3.cpp:48:63:   required from here
    expression_sfinae.3.cpp:27:99: error: no matching function for call to ‘std::__cxx11::list<bool>::list(<brace-enclosed initializer list>)’
                 return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                                                                                                       ^
    In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
                     from expression_sfinae.3.cpp:2:
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note: candidate: template<class _InputIterator, class> std::__cxx11::list<_Tp, _Alloc>::list(_InputIterator, _InputIterator, const allocator_type&)
             list(_InputIterator __first, _InputIterator __last,
             ^
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note:   template argument deduction/substitution failed:
    expression_sfinae.3.cpp:27:74: note:   cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
                 return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                                                                              ^
    In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
                     from expression_sfinae.3.cpp:2:
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
           list(initializer_list<value_type> __l,
           ^
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note:   candidate expects 2 arguments, 4 provided
    /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
           list(list&& __x) noexcept
           ^
u/5.3.0/include/g++-v5/bits/stl_list.h:697:9: note:   template argument deduction/substitution failed:
expression_sfinae.3.cpp:27:74: note:   cannot convert ‘detail::call_base_method_if_any<C, Z<A, B, C, D>, 1u>((*(Z<A, B, C, D>*)this), arg1, std::__cxx11::basic_string<char>(arg2))’ (type ‘void’) to type ‘const allocator_type& {aka const std::allocator<bool>&}’
             return std::list<bool> { ( detail::call_base_method_if_any<T>(*this, arg1, arg2)) ... }; 
                                                                          ^
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/list:63:0,
                 from expression_sfinae.3.cpp:2:
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = bool; _Alloc = std::allocator<bool>; std::__cxx11::list<_Tp, _Alloc>::allocator_type = std::allocator<bool>]
       list(initializer_list<value_type> __l,
       ^
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:678:7: note:   candidate expects 2 arguments, 4 provided
/usr/lib/gcc/x86_64-pc-linux-gnu/5.3.0/include/g++-v5/bits/stl_list.h:667:7: note: candidate: std::__cxx11::list<_Tp, _Alloc>::list(std::__cxx11::list<_Tp, _Alloc>&&) [with _Tp = bool; _Alloc = std::allocator<bool>]
       list(list&& __x) noexcept
       ^
// 9000 lines ommited

附言:我还没有弄清楚如何处理不返回任何内容的函数(我认为你不能有一个void列表)。欢迎提出任何建议。

问题的根本原因是C的方法返回void:
struct C {
    void base_method(int a, std::string b);
    ^^^^^
};

您正试图使用该返回类型来构建您的std::list<bool>

假设你想排除这种情况,我会这样重写你的details

// this is the preferred overload (last argument is 'int' instead of '...')
// but will be SFINAE-d out if base_method() isn't callable with int or string
// or it returns something other than bool
template <class Base, class T,
    class = std::enable_if_t<std::is_same<bool,
        decltype(std::declval<Base&>().base_method(
            std::declval<int>(),
            std::declval<std::string>()))
        >::value>>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, int)
{    
    return obj.Base::base_method(arg1, arg2);
}
// fallback overload
template <class Base, class T>
bool call_base_method_if_any_impl(T& obj, int arg1, std::string arg2, ...) {    
    return false;
}
template <class Base, class T>
bool call_base_method_if_any(T& obj, int arg1, std::string arg2) {
    return call_base_method_if_any_impl<Base>(obj, arg1, arg2, 0);
}