Search code examples
c++sfinaetmp

Possible Implementation of is_assignable


i'm trying to get used to some tmp concepts.

Here is one solution to check if 2 Types are assignable: This is the most important part:

template<typename _Tp, typename _Up>
class __is_assignable_helper: public __sfinae_types {

template<typename _Tp1, typename _Up1>
static decltype(std::declval<_Tp1>() = std::declval<_Up1>(), __one())
__test(int) {}

template<typename, typename>
static __two __test(...) {}

public:
static constexpr bool value = sizeof(__test<_Tp, _Up>(0)) == 1;
};

Now i tried to use some other sfinae tricks but it doesn't work ..

template<typename _Tp, typename _Up, typename = void>
class __is_assignable_helper2
{

public:
static constexpr bool value = false;
};


template<typename _Tp, typename _Up>
class __is_assignable_helper2<_Tp, _Up, 
                                typename std::enable_if<
                                std::is_same<typename
                                decltype(std::declval<_Tp>()=    std::declval<_Up>(), char)
,char >::value
                                                                     , void
                                                        >::type>
{

public:
 static constexpr bool value = true;
};

GCC says: error: wrong number of template arguments (1, should be2) >::value

.. He doesnt accept the decltype as template parameter in is_same.

Could someone explain this error message ? and suggest a solution ?

UPDATE:

OK ! It works ! i wrote char , instead of char{}....

Next Problem:

Is there a more elegant implementation ?


Solution

  • The is_assignable type trait is true if both the type U is assignable to the type T.

    To implement the condition, it is sufficient to verify whether the expression std::declval<T&>() = std::declval<U>() is well-formed. The type T is specified as an lvalue reference to work with primitive types too.

    Example:

    namespace detail {
      template <typename, typename, typename = void>
      struct is_assignable
       : std::false_type {};
    
      template <typename T, typename U>
      struct is_assignable<T, U, std::void_t<decltype(std::declval<T&>() = std::declval<U>())>>
       : std::true_type {};
    }
    
    template <typename T, typename U>
    struct is_assignable
     : detail::is_assignable<T, U> {};
    
    template <typename T, typename U>
    inline constexpr bool is_assignable_v = is_assignable<T, U>::value;
    

    It is possible to make the implementation shorter by removing the helper type traits in the detail namespace. However, they have been introduced in order that the effective type trait has only two template parameters. The purpose is to avoid that the user be able to accidentally specify three template arguments without receiving a compilation error.