I am working on downgrading a project written in C++ 17 to C++ 14. While downgrading, I came across a piece of code involving if constexpr
and I wish to convert it to C++ 14 (From what I know, if constexpr
is a C++ 17 feature).
Boost's is_detected
is used to check if a given type has star operator or get method.
#include <iostream>
#include <boost/type_traits/is_detected.hpp>
#include <type_traits>
#include <boost/optional/optional.hpp>
#include <memory>
#include <typeinfo>
template < template < typename... > typename Operation, typename... Args >
constexpr bool is_detected_v = boost::is_detected< Operation, Args... >::value;
template < typename T >
using has_star_operator = decltype( *std::declval< T >( ) );
template < typename T >
using has_get_method = decltype( std::declval< T >( ).get( ) );
There is a function call deref
which is used to dereference types like pointers, arrays, iterators, smart pointers, etc.
template < typename T >
inline constexpr const auto&
deref( const T& value )
{
if constexpr ( is_detected_v< has_star_operator, T > )
{
return deref( *value );
}
else if constexpr ( is_detected_v< has_get_method, T > )
{
return deref( value.get( ) );
}
else
{
return value;
}
}
I tried to form a solution without if constexpr
by using std::enable_if
as below:
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && is_detected_v<has_star_operator, T>,
decltype( *std::declval< const T >( ) )>::type
deref(const T& value)
{
std::cout << "STAR " << typeid(*value).name() << std::endl;
return *value;
}
template <typename T>
typename std::enable_if<
is_detected_v<has_get_method, T>,
decltype( std::declval< const T >( ).get( ) ) >::type
deref(const T& value)
{
std::cout << "GET " << typeid(value.get()).name() << std::endl;
return value.get();
}
template <typename T>
typename std::enable_if<
!is_detected_v<has_get_method, T> && !is_detected_v<has_star_operator, T>,
const T>::type
deref(const T& value)
{
std::cout << "NONE\n";
return value;
}
int main()
{
int VALUE = 42;
boost::optional<int> optional_value = boost::make_optional(VALUE);
int a = 42;
int *b = &a;
const int array[ 4 ] = {VALUE, 0, 0, 0};
//const auto list = {std::make_unique< int >( VALUE ), std::make_unique< int >( 0 ),
// std::make_unique< int >( 0 )};
//const auto iterator = list.begin( );
//std::unique_ptr<int> u = std::make_unique< int >( VALUE );
std::cout << deref(a) << std::endl;
std::cout << deref(optional_value) << std::endl;
std::cout << deref(b) << std::endl;
std::cout << deref(array) << std::endl;
//std::cout << deref(iterator) << std::endl;
//std::cout << deref(u) << std::endl;
}
But, the above fails for cases like iterators and smart pointers where multiple dereference has to be made. For example, for a std::unique_ptr
, first p.get()
will be called (auto q = p.get()
) followed by star operator (*q
).
I am a beginner with templates and require some help in this. Please let me know how this can be solved.
I am using GCC 5.4 to compile.