【问题标题】:method, that calls same-named method of all base classes, if exists and saves return value to a list方法,调用所有基类的同名方法(如果存在)并将返回值保存到列表中
【发布时间】:2016-03-27 18:45:20
【问题描述】:

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

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

下面是一个函数,由我的宏生成,它应该调用所有基的名为“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

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

【问题讨论】:

    标签: c++ templates c++14 variadic-templates sfinae


    【解决方案1】:

    问题的根本原因是C的方法返回void

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

    您正尝试使用该返回类型来构建您的std::list&lt;bool&gt;

    假设您想排除这种情况,我会像这样重写您的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);
    }
    

    【讨论】:

    • 酷。我仍然会尝试使用 decltype 作为返回值,因为在宏中它应该支持不同的返回类型,你的更改是救命的。
    • 不幸的是,您的解决方案仍然对我不起作用。问题是它没有解决“如果它不存在则跳过方法”部分。
    • @ScottTiger 确实如此。在您的示例中,没有一个类具有bool base_method(int, string) 函数,并且call_base_method_of_all_basese_if_any() 正确返回包含{false, false, false, false}list&lt;bool&gt;
    • 是的,你是对的,但想法是跳过不存在的方法调用,而不是返回默认值。在我最初的例子中就是这样。我目前结束了返回 unique_ptr 并检查它是否设置,但我不喜欢那样。
    猜你喜欢
    • 2017-09-11
    • 1970-01-01
    • 2011-10-05
    • 1970-01-01
    • 2012-05-02
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 2014-04-02
    相关资源
    最近更新 更多