SFINAE比赛未按预期进行

问题描述 投票:0回答:1

我在ubuntu上同时使用g ++ 7.5.0和clang 6.0.0来根据对象的方法存在尝试自动分派函数调用的SFINAE函数,并且结果不符合预期。

我期望的是,对于矢量容器,它应该在容器的销毁函数中调用矢量的clear方法。对于像int这样的基本类型,它除了打印出消息外什么也不做。但他们现在都给两个。我想知道这里怎么了。

#include <iostream>
#include <typeinfo>
#include <vector>

using namespace std;

template <typename T> struct has_clear {
    typedef char true_type;
    typedef int false_type;

    template <typename U, size_t (U::*)() const> struct SFINAE {
    };
    template <typename U> static char Test(SFINAE<U, &U::clear> *);
    template <typename U> static int Test(...);

    static const bool has_method = sizeof(Test<T>(nullptr) == sizeof(char));
    typedef decltype(Test<T>(nullptr)) ret_type;
    // typedef Test<T>(0) type_t;
};

template <typename T> class MyContainer {
    // using typename has_clear<T>::true_type;
    // using typename has_clear<T>::false_type;
    T _obj;

  public:
    MyContainer(const T &obj) : _obj(obj) {}
    // static void clear(MyContainer *m);
    void clear(const typename has_clear<T>::true_type t)
    {
        cout << "the " << typeid(_obj).name() << " object has clear() function!" << endl;
        cout << "typeid(t).name(): " << typeid(t).name() << endl;
        _obj.clear();
        cout << "clear has be done!" << endl;
    }
    void clear(const typename has_clear<T>::false_type t)
    {
        cout << "the " << typeid(_obj).name() << " object has no clear() function!" << endl;
        cout << "typeid(t).name(): " << typeid(t).name() << endl;
        cout << "just do nothing and quit!" << endl;
    }
    ~MyContainer()
    {
        cout << "has_clear<T>::true_type: " << typeid(typename has_clear<T>::true_type()).name()
             << endl;
        cout << "has_clear<T>::flase_type: " << typeid(typename has_clear<T>::false_type()).name()
             << endl;
        clear(typename has_clear<T>::ret_type());
    };
    // template <bool b> ~MyContainer();
};

int main()
{
    cout << "before MyContainer<vector<int>>" << endl;
    {
        vector<int> int_vec;
        MyContainer<vector<int>> int_vec_container(int_vec);
    }
    cout << "after MyContainer<vector<int>>" << endl;
    cout << "before MyContainer<int>" << endl;
    {
        MyContainer<int> int_container(1);
    }
    cout << "after MyContainer<int>" << endl;
}


它产生:

before MyContainer<vector<int>>
has_clear<T>::true_type: FcvE
has_clear<T>::flase_type: FivE
the St6vectorIiSaIiEE object has no clear() function!
typeid(t).name(): i
just do nothing and quit!
after MyContainer<vector<int>>
before MyContainer<int>
has_clear<T>::true_type: FcvE
has_clear<T>::flase_type: FivE
the i object has no clear() function!
typeid(t).name(): i
just do nothing and quit!
after MyContainer<int>
c++ templates sfinae
1个回答
0
投票

我不知道您的实现has_clear出了什么问题,但是可以使用更现代的SFINAE / type_traits功能将其替换为这种大大简化且有效的实现:

template<typename T, typename Enable = void>
struct has_clear : std::false_type {};

template<typename T>
struct has_clear<
    T,
    std::enable_if_t<
        std::is_member_function_pointer_v<decltype(&T::clear)>
    >
> : std::true_type {};

为方便起见:

template<typename T>
constexpr bool has_clear_v = has_clear<T>::value;

if constexpr结合使用,您可以非常简洁地确定在其他人无法编译时运行哪个代码路径。例如:

template<typename T>
void maybe_clear(T t){
    if constexpr (has_clear_v<T>){
        // only compiled when T has a non-static clear() method
        std::cout << "clearing " << typeid(T).name() << '\n';
        t.clear();
    } else {
        // only compiled when T does not have a non-static clear() method
        std::cout << "doing nothing with " << typeid(T).name() << '\n';
    }
}

我相信这可以达到您想要的目标,但是如果我误解了,请更正。此解决方案以需要C ++ 17为代价。

Live Demo

© www.soinside.com 2019 - 2024. All rights reserved.