方法,调用所有基 类 的同名方法,如果存在并将 return 值保存到列表

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

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

我设法在不保留这些方法的 return 值的情况下让它工作。 现在我想保存这些值和 return 一个列表。

下面是一个函数,由我的宏生成,它应该调用所有基数的名为 "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 << "A\n"; return true; }
    bool base_method_narg(){ std::cout << "A no arg\n"; return true; }
};

struct B{ void base_method(int, bool){ std::cout << "B\n"; } };
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

P.S。我还没有想出如何处理函数,它不 return 任何东西(我不认为你可以有一个 void 列表)。欢迎任何建议。

问题的根源在于C的方法returns void:

struct C {
    void base_method(int a, std::string b);
    ^^^^^
};

并且您正在尝试使用 return 类型来构建您的 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);
}