Apply SFINAE to check if the attribute is defined for T

I am currently working on a small math vector class. I want two class vectors, Vector2and Vector3be constructive from one to the other. For example:

Vector2<float> vec2(18.5f, 32.1f); // x = 18.5; y = 32.1
Vector3<float> vec3(vec2);         // x = 18.5; y = 32.1; z = float()

To do this and facilitate extensibility, I would like to use features VectorTraitswith its basic definition:

template <typename T>
struct VectorTraits
{
  typedef T                      VectorType;
  typedef typename T::ValueType  ValueType;

  static const unsigned int      dimension = T::dimension;
};

This form will allow the user to establish a relationship between existing Vector classes (e.g. glm :: vec2, for example) and my classes. Then it would be possible to create Vector2 from glm :: vec2.

In addition, this method allowed me to write a generic streaming operator for all classes defining VectorTraitsusing SFINAE.

, operator<<, , VectorTraits .

( ):

#include <iostream>
#include <type_traits>

// To define another operator
struct Dummy
{};

// Traits class
template <typename T>
struct VectorTraits
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};

// Fake vector class. Defines the required typedef.
struct Vec
{
  typedef float   ValueType;
  static const std::uint16_t dimension = 2;
};

// Streaming operator for Dummy.
std::ostream& operator<<(std::ostream& stream, const Dummy& d)
{
    stream << "dummy.\n";
    return stream;
}

// Streaming operator attempt for classes defining VectorTraits.
template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>
std::ostream& operator<<(std::ostream& stream, const T& vec)
{
    std::cout << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

int main()
{
    std::cout << "Test\n";
    std::cout << Vec();
    std::cout << Dummy();
    return 0;
}

error: no match for 'operator<<' (operand types are 'std::ostream {aka std::basic_ostream<char>}' and 'Vec')
prog.cpp:33:15: note: candidate: template<class T, typename std::enable_if<(VectorTraits<T>::dimension > 0), void>::type <anonymous> > std::ostream& operator<<(std::ostream&, const T&)
 std::ostream& operator<<(std::ostream& stream, const T& vec)
               ^
prog.cpp:33:15: note:   template argument deduction/substitution failed:
prog.cpp:41:19: note:   couldn't deduce template parameter '<anonymous>'

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>>

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = 0>

prog.cpp:13:35: error: 'char [21]' is not a class, struct, or union type
   typedef typename T::ValueType   ValueType;

, , VectorTraits, Vector. "" Vector typedef.

, - . , , .

+4
4

, Jarod42, Visual Studio, . , , :

VectorTraits :

template <typename T, typename = void>
struct VectorTraits;

, T::dimension > 0:

template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};

, , , std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>:

template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
    return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

:

#include <iostream>
#include <type_traits>

// Traits class
template <typename T, typename = void>
struct VectorTraits;

template <typename T>
struct VectorTraits<T, std::enable_if_t<(T::dimension > 0)>>
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};


// Fake vector class. Defines the required typedef.
struct Vec
{
  typedef float   ValueType;
  static const std::uint16_t dimension = 2;
};

// Fake vector class. Defines the required typedef.
struct VecFake
{
};

template <>
struct VectorTraits<VecFake>
{
  typedef VecFake                       VectorType;
  typedef float   ValueType;
  static const std::uint16_t      dimension = 12;
};

// Streaming operator attempt for classes defining VectorTraits.
template <class T, typename = std::enable_if_t<(sizeof(VectorTraits<T>) > 0)>>
std::ostream& operator << (std::ostream& stream, const T& )
{
    return stream << "Traits. Dimension = " << VectorTraits<T>::dimension << "\n";
}

int main()
{
    std::cout << "Test\n";  // Test
    std::cout << Vec();     // Traits. Dimension = 2
    std::cout << VecFake(); // Traits. Dimension = 12
}
0

, std::enable_if_t, . - * = nullptr:

template <class T, std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)

, VectorTraits<T> T::ValueType. SFINAE, , . , SFINAE :

template <class T, typename = typename T::ValueType, 
          std::enable_if_t<(VectorTraits<T>::dimension > 0)>* = nullptr>
std::ostream& operator<<(std::ostream& stream, const T& vec)

IsValidVector, , .

+4

, :

// Traits class
template <typename T, typename = void>
struct VectorTraits_impl
{
};


template <typename T>
struct VectorTraits_impl<T,
                        std::enable_if_t<std::is_integral<decltype(T::dimension)>::value>>
{
  typedef T                       VectorType;
  typedef typename T::ValueType   ValueType;
  static const std::uint16_t      dimension = T::dimension;
};

// Traits class
template <typename T>
using VectorTraits = VectorTraits_impl<T>;

+4

.

For your vectors std::enable_if_t<(VectorTraits<T>::dimension > 0)>, this is some type. Therefore, you declare operator<<as a template where the second parameter is a value of this type. There is no such type for Dummy(and for char*), therefore SFINAE excludes this. But for Vecyou std::enable_if_t<(VectorTraits<T>::dimension > 0)>to have some kind of type, and the compiler expects the template parameter to be some value of that type. Of course, he has no way of knowing what that meaning is.

+1
source

Source: https://habr.com/ru/post/1611732/


All Articles