0

Ηello I write a code in C++ using templates and I try to implement a SFINAE what is fired when operator + is not supported. I write the following code

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

I expect the constexpr bool value = HasPlusT<B,B>::value to return false but an error is generated What is wrong in implementation?

Class B has not an operator + and I expect that the constexpr bool value = >HasPlusT::value returns true. However a compilation error is generated no match for 'operator+' (operand types are 'HasPlusT::T1_t {aka B}' and >'HasPlusT::T2_t {aka B}') Demo.cpp /Demo C/C++ >Problem

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

New implementation to support class templates. Why the following implementatio fail to verify that operator + exists

#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;
}
getsoubl
  • 808
  • 10
  • 25
  • Possible duplicate of [Is it possible to write a template to check for a function's existence?](https://stackoverflow.com/questions/257288/is-it-possible-to-write-a-template-to-check-for-a-functions-existence) – Andriy Tylychko Aug 23 '18 at 22:12

1 Answers1

1

Try with

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

I mean... your code has a couple of problems.

In no particular order.

1 - If you call test<T1,T2>(nullptr), you explicitly pass two template types; so if you define test with a second type for the second parameter

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

the second one is never used.

2 - SFINAE works with template parameter of the function; not with template parameter of the class. So if you try something as

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

SFINAE doesn't works because you're not using U1 and U2 (template parameter of the method) but with T1_t() and T2_t(), so T1 and T2, template parameters of the class.

So I suggest the use of a using Rr to remove reference

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

and, to make simpler, to use SFINAE through returned type

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

-- EDIT --

The OP can't use std::declval().

I use mingw and for an unknown reason cannot recognize declval

So I propose a better-than-nothing trivial substitute (without std::add_rvalue_reference<T>::type, in case you can't use it)

template <typename T>
T declVal();

and HasPlusT become

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

-- EDIT 2 --

The OP say

what modifications are needed to support check for class templates? I try to do this for a class A but seems not to work. See in the initial post.

Isn't a problem of class templates.

The problem is that your operator+() for A classes

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

is unusual because is (correctly) a friend function of the class but receive references (left-references), not constant, to A<T> objects (usually are const references; the first value can be a value not referenced), modify the first received argument (dangerous) and return it by copy.

So the HasPlusT class fail because std::declval() return a r-value referenced object that doesn't match an operator+ that ask for l-values.

I strongly suggest you to modify operator+() as follows (a1 and a2 const)

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

or, maybe better, as follows (a1 without &, a2 const)

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

and you'll see that HasPlusT works again.

max66
  • 65,235
  • 10
  • 71
  • 111
  • Hm. There's a reason all those type-traits inherit from `std::bool_constant`. – Deduplicator Aug 23 '18 at 22:05
  • Declval is not supported in my compiler and I try this alternative..But I try to understand where is the error – getsoubl Aug 23 '18 at 22:07
  • 1
    @GeorgeTsoumplekas: `std::declval`'s implementation is completely trivial; it's just the declaration. But you really should say if you don't even have a minimal C++11 implementation. – Deduplicator Aug 23 '18 at 22:09
  • @GeorgeTsoumplekas - No `std::declval`? Really? Have you included `utility`? Which compiler are you using? – max66 Aug 23 '18 at 22:11
  • I use mingw and for an unknown reason cannot recognize declval – getsoubl Aug 23 '18 at 22:18
  • @GeorgeTsoumplekas - Answer modified with a better-than-nothing substitute for `std::declval()` – max66 Aug 23 '18 at 22:24
  • @max66 what modifications are needed to support check for class templates? I try to do this for a class A but seems not to work. See in the initial post – getsoubl Aug 24 '18 at 18:52
  • @GeorgeTsoumplekas - I'm surprised: it should works also with redefined `operator+()`. In this moment I can't but, within hours, I'll try to understand what's wrong. – max66 Aug 24 '18 at 19:17
  • Ιt works for normal classes. But for classes template does not work. Thank you for support – getsoubl Aug 24 '18 at 19:27
  • @GeorgeTsoumplekas - It's a reference problem; not a template-class problem; your `operator+()` is all but canonical; "Edit 2" added in answer. – max66 Aug 24 '18 at 21:28
  • Thank you the answer.But I am little confused. declval in any case will return r-value.What rule is applied and is needed the first argument to be a value and no the two of them? Why when use a temp object for add (fist approach)there is no problem .. It is strange for me – getsoubl Aug 24 '18 at 22:02
  • @getsoubl - I don't understandn what do you exactly want to know; anyway... a r-value is compatible with a function that accept both a `T const &` (by const reference) object or a `T` (by copy) object. – max66 Aug 24 '18 at 22:26
  • Great. I miss the point that r-value is compatible with const & – getsoubl Aug 24 '18 at 22:35