【问题标题】:SFINAE Based traits to determine if operator + is supported基于 SFINAE 的特征来确定是否支持运算符 +
【发布时间】:2019-01-30 08:43:47
【问题描述】:

你好 我使用模板在 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&lt;B,B&gt;::value 返回 false 但会生成错误 执行中有什么问题?

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

===============================================

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

#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;
}

【问题讨论】:

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


【解决方案1】:

试试

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&lt;T1,T2&gt;(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(),所以T1T2,模板参数为班级。

所以我建议使用using Rr 来删除引用

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

为了简单起见,通过返回类型使用 SFINAE

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

-- 编辑--

OP 不能使用std::declval()

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

所以我提出了一个胜过一切的微不足道的替代品(没有std::add_rvalue_reference&lt;T&gt;::type,以防你不能使用它)

template <typename T>
T declVal();

HasPlusT 变成

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;
 };

-- 编辑 2--

OP 说

需要哪些修改来支持检查类模板?我尝试为 A 类执行此操作,但似乎不起作用。请参阅最初的帖子。

不是类模板的问题。

问题是你的operator+() 用于A

friend A<T> operator + (A<T> & a1, A<T> & a2)
 { return a1.add(a2); }

是不寻常的,因为它(正确地)是类的 friend 函数,但接收到 A&lt;T&gt; 对象的引用(左引用)而不是常量(通常是 const 引用;第一个值可以是一个值未引用),修改第一个收到的参数(危险)并通过复制返回。

所以HasPlusT 类失败了,因为std::declval() 返回的右值引用对象与请求左值的operator+ 不匹配。

强烈建议你修改operator+()如下(a1a2const

  friend A<T> operator+ (A<T> const & a1, A<T> const & a2)
   { A<T> ret{a1}; return ret.add(a2); }   

或者,也许更好,如下(a1 没有&amp;a2 const

  friend A<T> operator+ (A<T> a1, A<T> const & a2)
   { return a1.add(a2); }

您会看到HasPlusT 再次起作用。

【讨论】:

  • 嗯。所有这些类型特征都继承自 std::bool_constant 是有原因的。
  • 我的编译器不支持Declval,我尝试了这个替代方案。但我试图了解错误在哪里
  • @GeorgeTsoumplekas:std::declval 的实现完全是微不足道的;这只是声明。但你真的应该说,如果你甚至没有最小的 C++11 实现。
  • @GeorgeTsoumplekas - 否 std::declval?真的吗?您是否包含utility?你用的是哪个编译器?
  • 我使用 mingw,但由于未知原因无法识别 declval
猜你喜欢
  • 2015-03-05
  • 1970-01-01
  • 2011-08-27
  • 1970-01-01
  • 2022-06-29
  • 1970-01-01
  • 2012-04-25
  • 2012-02-11
  • 2011-07-25
相关资源
最近更新 更多