基于SFINAE的特征来确定是否支持运算符+

时间:2018-08-23 21:52:53

标签: c++ c++11 templates sfinae typetraits

你好 我使用模板使用C ++编写了代码,并尝试实现SFINAE,这在不支持运算符+时触发。 我写下面的代码

#include <iostream>
#include <type_traits>
class B
{
};
template<typename T1,typename T2>
struct IsSameT :std::false_type
{
    //static constexpr bool value = false;
};

template<typename T>
struct IsSameT<T,T> :std::true_type
{
    //static constexpr bool value = true;
};
template<typename T1, typename T2>
struct HasPlusT
{
    private:
    using T1_t = typename std::remove_reference<T1>::type;
    using T2_t = typename std::remove_reference<T2>::type;
    template<typename U1, typename = decltype(T1_t() + T2_t())>
    static char test(void *);
    template<typename>
    static long test(...);
    public:
    static constexpr  bool value = IsSameT<decltype(test<T1,T2>(nullptr)),char>::value;
};

template<typename T1,typename T2, bool = HasPlusT<T1,T2>::value>
struct PlusResultT
{
    using T1_t = typename std::remove_reference<T1>::type;
    using T2_t = typename std::remove_reference<T2>::type;
    using Type = decltype(T1_t() + T2_t());
};

template<typename T1,typename T2>
struct PlusResultT<T1,T2,false>
{
    using T1_t = typename std::remove_reference<T1>::type;
    using T2_t = typename std::remove_reference<T2>::type;
    using Type = decltype(T1_t() + T2_t());
};


int main()
{

    constexpr bool value = HasPlusT<B,B>::value;
    return 0;
}

我希望constexpr bool value = HasPlusT<B,B>::value返回false,但是会生成错误 实施中有什么问题?

  

B类没有运算符+,我希望constexpr bool值=> HasPlusT :: value返回true。但是会生成编译错误   与'operator +'不匹配(操作数类型为'HasPlusT :: T1_t {aka B}'和>'HasPlusT :: T2_t {aka B}'))Demo.cpp / Demo C / C ++>问题

============================================= < / p>

支持类模板的新实现。为什么以下实现无法验证运算符+存在

#include <iostream>
#include <type_traits>
#include <array>
#include <vector>
#include <utility>

template<typename T1,typename T2>
struct IsSameT :std::false_type
{
    //static constexpr bool value = false;
};

template<typename T>
struct IsSameT<T,T> :std::true_type
{
    //static constexpr bool value = true;
};

template<typename T, typename U>
struct IsFuntamentalHelper : IsSameT<T,U>
{
    //static constexpr bool value = IsSameT<T,U>::value;
};
template<typename T>
struct IsFundamentalT : std::false_type
{
    //static constexpr boo value = std::false_type;
};

template<>
struct IsFundamentalT<int> : IsFuntamentalHelper<int,int>
{

};
template<>
struct IsFundamentalT<float> : IsFuntamentalHelper<float,float>
{

};
template<>
struct IsFundamentalT<double> : IsFuntamentalHelper<double,double>
{

};
template<>
struct IsFundamentalT<long>: IsFuntamentalHelper<long,long>
{

};

template<typename T>
using enable_if_t = typename std::enable_if<IsFundamentalT<T>::value, T>::type;

template<typename T>
class A
{
public:
    template<typename = enable_if_t<T>>
    operator T()
    {
          return t ;
    }
    A<T>():t()
    {

    }
    template<typename = enable_if_t<T>>
    A<T>(T   a)
    {
        std::cout << "integer" << std::endl;
        this->t = a;
    }
    A<T>(A<T>  const & a)
    {
        this->t = a.t;

    }
public:
    A<T> add(A<T>  & a)
    {
        t += a.t;
        return *this;

    }
    friend  A<T>  operator + (A<T>   & a1, A<T>   &  a2)
    {

        return a1.add(a2);
    }
    T t;
};
template<typename T1, typename T2>
struct HasPlusT
{
    private:
    template <typename T>
          using Rr = typename std::remove_reference<T>::type;

          template<typename U1, typename U2>
      static auto test(void *)
         -> decltype(std::declval<Rr<U1>>() + std::declval<Rr<U2>>() , '0');

          template<typename...>
          static long test(...);

       public:
          static constexpr bool value
             = IsSameT<decltype(test<T1,T2>(nullptr)),char>::value;
};


template<typename T1,typename T2, bool = HasPlusT<T1,T2>::value>
struct PlusResultT
{
    using T1_t = typename std::remove_reference<T1>::type;
    using T2_t = typename std::remove_reference<T2>::type;
    using Type = decltype(T1_t() + T2_t());
};

template<typename T1,typename T2>
struct PlusResultT<T1,T2,false>
{
    using T1_t = typename std::remove_reference<T1>::type;
    using T2_t = typename std::remove_reference<T2>::type;
    using Type = decltype(T1_t() + T2_t());
};


int main()
{
    constexpr bool value = HasPlusT<A<int>,A<int>>::value;
    std::cout << value << std::endl;


    return 0;
}

1 个答案:

答案 0 :(得分:0)

尝试

template <typename T1, typename T2>
struct HasPlusT
 {
   private:
      template <typename T>
      using Rr = typename std::remove_reference<T>::type;

      template<typename U1, typename U2>
      static auto test(void *)
         -> decltype(std::declval<Rr<U1>>() + std::declval<Rr<U2>>() , '0');

      template<typename...>
      static long test(...);

   public:
      static constexpr bool value
         = IsSameT<decltype(test<T1,T2>(nullptr)),char>::value;
 };

我的意思是……您的代码有几个问题。

无特定顺序。

1-如果调用test<T1,T2>(nullptr),则显式传递两种模板类型;即:因此,如果您为第二个参数定义第二种类型的test

template<typename U1, typename = decltype(T1_t() + T2_t())>
    static char test(void *);

第二个从未使用过。

2-SFINAE与函数的模板参数一起使用;不带有该类的模板参数。因此,如果您尝试使用

template<typename U1, typename U2, typename = decltype(T1_t() + T2_t())>
    static char test(void *);

SFINAE不起作用,因为您没有使用U1U2,而是使用T1_t()T2_t()(方法的模板参数),所以{{1 }}和T1,该类的模板参数。

因此,我建议使用T2 using删除引用

Rr

并且为了简化起见,通过返回的类型使用SFINAE

      template <typename T>
      using Rr = typename std::remove_reference<T>::type;

-编辑-

OP无法使用 template<typename U1, typename U2> static auto test(void *) -> decltype(std::declval<Rr<U1>>() + std::declval<Rr<U2>>() , '0');

  

我使用mingw,并且由于未知原因无法识别declval

因此,我提出了一个总比没有强的琐碎替代品(如果没有使用,请使用std::declval()

std::add_rvalue_reference<T>::type

template <typename T> T declVal(); 成为

HasPlusT

-编辑2-

OP说

  

需要哪些修改才能支持检查类模板?我尝试对A类进行此操作,但似乎不起作用。请参阅初始文章。

这不是类模板的问题。

问题是您为template <typename T1, typename T2> struct HasPlusT { private: template <typename T> using Rr = typename std::remove_reference<T>::type; template<typename U1, typename U2> static auto test(void *) -> decltype(declVal<Rr<U1>>() + declVal<Rr<U2>>() , '0'); template<typename...> static long test(...); public: static constexpr bool value = IsSameT<decltype(test<T1,T2>(nullptr)),char>::value; }; 类学习的operator+()

A

是不寻常的,因为(正确地)是类的friend A<T> operator + (A<T> & a1, A<T> & a2) { return a1.add(a2); } 函数,但是接收到friend对象的引用(左引用),而不是常量(通常是A<T>引用;第一个值可以是未引用的值),修改接收到的第一个参数(危险)并通过复制返回。

因此const类失败是因为HasPlusT返回的r值引用对象与要求l值的std::declval()不匹配。

强烈,建议您如下修改operator+operator+()a1 a2

const

或者可能更好,如下({ friend A<T> operator+ (A<T> const & a1, A<T> const & a2) { A<T> ret{a1}; return ret.add(a2); } 不包含a1& a2

const

,您将看到 friend A<T> operator+ (A<T> a1, A<T> const & a2) { return a1.add(a2); } 再次起作用。