2016-11-05 15 views
7

Базовые компоненты моей библиотеки хобби должны работать с компиляторами C++ 98 и C++ 11. Чтобы учиться и наслаждаться, я создал реалии C++ 98 с поддержкой нескольких типов (например, enable_if, conditional, is_same, is_integral и т. Д.), Чтобы использовать их, когда нет поддержки C++ 11.C++ 98/03 std :: is_constructible реализация

Однако, пока я реализовывал is_constructible Я застрял. Есть ли какая-либо магия шаблона (какой-то SFINAE), с которой я могу реализовать ее без поддержки C++ 11 (declval)?

Конечно, в C++ 03 нет поддержки шаблонов шаблонов, поэтому я буду специализироваться на реализации до некоторой глубины. Главный вопрос заключается в том, есть ли метод, который может решить, является ли T конструктивным из данных типов или нет.

+6

Несколько компиляторов используют встроенный помощник '__is_constructible', поэтому, вероятно, это очень сложно, как минимум. –

+0

Связанный: https://stackoverflow.com/questions/38181357/how-is-stdis-constructiblet-args-implemented – BartoszKP

+0

Да, реализация C++ 11 понятна. Я хотел бы исключить зависимости 'decltype' и' std :: declval' от упомянутой реализации с какой-то магией или найти совершенно другую технику. Единственным ограничением является стандарт C++ 98. – Broothy

ответ

5

Возможно:

#include <iostream> 

template<typename T, T Val> 
struct integral_constant { 
    typedef integral_constant type; 
    typedef T value_type; 
    enum { 
     value = Val 
    }; 
}; 

typedef integral_constant<bool, true> true_type; 
typedef integral_constant<bool, false> false_type; 

template<typename T> 
struct remove_ref { 
    typedef T type; 
}; 

template<typename T> 
struct remove_ref<T&> { 
    typedef T type; 
}; 

// is_base_of from https://stackoverflow.com/questions/2910979/how-does-is-base-of-work 
namespace aux { 
    typedef char yes[1]; 
    typedef char no[2]; 

    template <typename B, typename D> 
    struct Host 
    { 
     operator B*() const; 
     operator D*(); 
    }; 
} 
template <typename B, typename D> 
struct is_base_of 
{ 
    template <typename T> 
    static aux::yes& check(D*, T); 
    static aux::no& check(B*, int); 

    static const bool value = sizeof(check(aux::Host<B,D>(), int())) == sizeof(aux::yes); 
}; 

template<typename T> 
struct remove_cv { 
    typedef T type; 
}; 
template<typename T> 
struct remove_cv<const T> { 
    typedef T type; 
}; 
template<typename T> 
struct remove_cv<volatile T> { 
    typedef T type; 
}; 
template<typename T> 
struct remove_cv<const volatile T> { 
    typedef T type; 
}; 

template<typename T> 
struct is_void : integral_constant<bool, false> {}; 
template<> 
struct is_void<void> : integral_constant<bool, true> {}; 

template <bool, typename T, typename> 
struct conditional { 
    typedef T type; 
}; 
template <typename T, typename U> 
struct conditional<false, T, U> { 
    typedef U type; 
}; 


namespace aux { 

template<typename T, typename U> 
struct is_more_const : integral_constant<bool, false> {}; 

template<typename T, typename U> 
struct is_more_const<const T, U> : integral_constant<bool, true> {}; 

template<typename T, typename U> 
struct is_more_const<const T, const U> : integral_constant<bool, false> {}; 

template<typename T, typename U> 
struct is_more_volatile : integral_constant<bool, false> {}; 

template<typename T, typename U> 
struct is_more_volatile<volatile T, U> : integral_constant<bool, true> {}; 

template<typename T, typename U> 
struct is_more_volatile<volatile T, volatile U> : integral_constant<bool, false> {}; 

template<typename T, typename U> 
struct is_more_cv : integral_constant<bool, is_more_const<T,U>::value && is_more_volatile<T,U>::value> {}; 


    template<typename T> 
    struct is_default_constructible { 
     template<typename U> 
     static yes& test(int(*)[sizeof(new U)]); 
     template<typename U> 
     static no& test(...); 
     enum { 
      value = sizeof(test<T>(0)) == sizeof(yes) 
     }; 
    };  

    template<typename T, typename Arg> 
    struct is_constructible_1 { 
     template<typename U, typename Arg_> 
     static yes& test(int(*)[sizeof(U(static_cast<Arg_>(*((typename remove_ref<Arg_>::type*)0))))]); 
     template<typename U, typename Arg_> 
     static no& test(...); 
     enum { 
      value = sizeof(test<T, Arg>(0)) == sizeof(yes) 
     }; 
    }; 

    // Base pointer construct from Derived Pointer 
    template<typename T, typename U> 
    struct is_constructible_1<T*, U*> 
     : conditional< 
      is_void<typename remove_cv<T>::type>::value, 
      integral_constant<bool, true>, 
      typename conditional< 
       is_void<typename remove_cv<U>::type>::value, 
       integral_constant<bool, false>, 
       typename conditional< 
        is_more_cv<T, U>::value, 
        integral_constant<bool, false>, 
        is_base_of<T,U> 
       >::type 
      >::type 
     >::type 
    {}; 

    // Base pointer construct from Derived Pointer 
    template<typename T, typename U> 
    struct is_constructible_1<T&, U&> 
     : conditional< 
      is_more_cv<T, U>::value, 
      integral_constant<bool, false>, 
      is_base_of<T,U> 
     >::type 
    {}; 


    template<typename T, typename Arg1, typename Arg2> 
    struct is_constructible_2 { 
     template<typename U, typename Arg1_, typename Arg2_> 
     static yes& test(int(*)[ 
      sizeof(U(
       static_cast<Arg1_>(*((typename remove_ref<Arg1_>::type*)0)), 
       static_cast<Arg2_>(*((typename remove_ref<Arg2_>::type*)0)) 
       )) 
      ]); 
     template<typename U, typename Arg1_, typename Arg2_> 
     static no& test(...); 
     enum { 
      value = sizeof(test<T, Arg1, Arg2>(0)) == sizeof(yes) 
     }; 
    }; 
} 

template<typename T, typename Arg1 = void, typename Arg2 = void> 
struct is_constructible : integral_constant<bool, aux::is_constructible_2<T, Arg1, Arg2>::value> { 

}; 

template<typename T, typename Arg> 
struct is_constructible<T, Arg> : integral_constant<bool, aux::is_constructible_1<T, Arg>::value> { 

}; 
template<typename T> 
struct is_constructible<T> : integral_constant<bool, aux::is_default_constructible<T>::value> { 

}; 

struct Foo {}; 
struct fuzz_explicit {}; 
struct fuzz_implicit {}; 
struct Fuzz { 
    explicit Fuzz(fuzz_explicit); 
    Fuzz(fuzz_implicit); 
}; 
struct buzz_explicit {}; 
struct buzz_implicit {}; 
struct Buzz { 
    explicit Buzz(buzz_explicit); 
    Buzz(buzz_implicit); 
}; 
struct Bar { 
    Bar(int); 
    Bar(int, double&); 
    Bar(Fuzz); 
    explicit Bar(Buzz); 
}; 

struct Base {}; 
struct Derived : Base {}; 

#define TEST(X) std::cout << #X << X << '\n' 

int main() { 
    TEST((is_constructible<Foo>::value)); 
    TEST((is_constructible<Bar>::value)); 
    TEST((is_constructible<Foo, int>::value)); 
    TEST((is_constructible<Bar, int>::value)); 
    TEST((is_constructible<Foo, const Foo&>::value)); 
    TEST((is_constructible<Bar, Bar>::value)); 
    TEST((is_constructible<Bar, int, double>::value)); 
    TEST((is_constructible<Bar, int, double&>::value)); 
    TEST((is_constructible<Bar, int, const double&>::value)); 
    TEST((is_constructible<int*, void*>::value)); 
    TEST((is_constructible<void*, int*>::value)); 
    TEST((is_constructible<Base&, Derived&>::value)); 
    TEST((is_constructible<Derived*, Base*>::value)); 
    // via Fuzz 
    TEST((is_constructible<Bar, fuzz_explicit>::value)); 
    TEST((is_constructible<Bar, fuzz_implicit>::value)); 
    // via Buzz 
    TEST((is_constructible<Bar, buzz_explicit>::value)); 
    TEST((is_constructible<Bar, buzz_implicit>::value)); 
    // integer promotion 
    TEST((is_constructible<Bar, char>::value)); 
    // integer conversion 
    TEST((is_constructible<Bar, unsigned long>::value)); 
} 

Вы можете расширить версию 2 параметров для 3, 4, 5, ... далее параметров больше.

Live Demo


Это работает с g++ 4.4.7

Он не работает с г ++ 4.3.6

+2

Ваша версия использует выражение SFINAE неявно. Хотя C++ 03 не кажется (явно?) Запретить его, многие реализации не поддерживали его в те дни. Так что это действительно не удовлетворительное решение. – Columbo

+0

@Columbo Он работает с g ++ 4.4.7, но да, он не работает с g ++ 4.3.6 – Danh

+0

что делать, если конструктор закрыт? –

4

Я думаю, что идея Danh было здорово! С незначительной модификацией мы можем исключить оператор new. (У меня есть реализация C++ 98 enable_if и remove_reference). Указанный случай int *, void * также работает с этой реализацией. Новый оператор не требуется. Остается только старая поддержка g ++ ...

/********** std::remove_cv replacement **********/ 
template< typename T > 
struct remove_const 
{ 
    typedef T type; 
}; 

template< typename T > 
struct remove_const< const T > 
{ 
    typedef T type; 
}; 


template< typename T > 
struct remove_volatile 
{ 
    typedef T type; 
}; 

template< typename T > 
struct remove_volatile< volatile T > 
{ 
    typedef T type; 
}; 


template< typename T > 
struct remove_cv 
{ 
    typedef typename remove_volatile< typename remove_const<T>::type >::type type; 
}; 


/********** std::is_pointer replacement *********/ 
template< typename T > 
struct is_pointer_helper 
{ 
    static const bool value = false; 
}; 

template< typename T > 
struct is_pointer_helper< T* > 
{ 
    static const bool value = true; 
}; 

template< typename T > 
struct is_pointer 
{ 
    static const bool value = is_pointer_helper< typename remove_cv<T>::type >::value; 
}; 


/********** std::enable_if replacement **********/ 
template< bool CONDITION, typename TYPE = void > 
struct enable_if 
{ 
}; 

template< typename TYPE > 
struct enable_if< true, TYPE > 
{ 
    typedef TYPE type; 
}; 


/****** std::remove_reference replacement *******/ 
template< typename T > 
struct remove_reference 
{ 
    typedef T type; 
}; 

template< typename T > 
struct remove_reference< T& > 
{ 
    typedef T type; 
}; 


/******* std::is_constructible replacement ******/ 
template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > 
class is_constructible_impl 
{ 
private: 
    template< typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3, typename C_AT_4 > 
    static bool test(
     typename c_std::enable_if< 
      sizeof(C_T) == 
      sizeof(C_T(
       static_cast<C_AT_1>(*static_cast< typename c_std::remove_reference<C_AT_1>::type* >(NULL)), 
       static_cast<C_AT_2>(*static_cast< typename c_std::remove_reference<C_AT_2>::type* >(NULL)), 
       static_cast<C_AT_3>(*static_cast< typename c_std::remove_reference<C_AT_3>::type* >(NULL)), 
       static_cast<C_AT_4>(*static_cast< typename c_std::remove_reference<C_AT_4>::type* >(NULL)) 
      )) 
     >::type* 
    ); 

    template< typename, typename, typename, typename, typename > 
    static int test(...); 

public: 
    static const bool value = (sizeof(test< T, AT_1, AT_2, AT_3, AT_4 >(NULL)) == sizeof(bool)); 
}; 

template< typename T, typename AT_1, typename AT_2, typename AT_3 > 
class is_constructible_impl< T, AT_1, AT_2, AT_3, void > 
{ 
private: 
    template< typename C_T, typename C_AT_1, typename C_AT_2, typename C_AT_3 > 
    static bool test(
     typename c_std::enable_if< 
      sizeof(C_T) == 
      sizeof(C_T(
       static_cast<C_AT_1>(*static_cast< typename c_std::remove_reference<C_AT_1>::type* >(NULL)), 
       static_cast<C_AT_2>(*static_cast< typename c_std::remove_reference<C_AT_2>::type* >(NULL)), 
       static_cast<C_AT_3>(*static_cast< typename c_std::remove_reference<C_AT_3>::type* >(NULL)) 
      )) 
     >::type* 
    ); 

    template< typename, typename, typename, typename > 
    static int test(...); 

public: 
    static const bool value = (sizeof(test< T, AT_1, AT_2, AT_3 >(NULL)) == sizeof(bool)); 
}; 

template< typename T, typename AT_1, typename AT_2 > 
class is_constructible_impl< T, AT_1, AT_2, void, void > 
{ 
private: 

    template< typename C_T, typename C_AT_1, typename C_AT_2 > 
    static bool test(
     typename c_std::enable_if< 
      sizeof(C_T) == 
      sizeof(C_T(
       static_cast<C_AT_1>(*static_cast< typename c_std::remove_reference<C_AT_1>::type* >(NULL)), 
       static_cast<C_AT_2>(*static_cast< typename c_std::remove_reference<C_AT_2>::type* >(NULL)) 
      )) 
     >::type* 
    ); 

    template< typename, typename, typename > 
    static int test(...); 

public: 
    static const bool value = (sizeof(test< T, AT_1, AT_2 >(NULL)) == sizeof(bool)); 
}; 

template< typename T, typename AT_1 > 
class is_constructible_impl< T, AT_1, void, void, void > 
{ 
private: 
    template< typename C_T, typename C_AT_1 > 
    static bool test(
     typename c_std::enable_if< 
      sizeof(C_T) == 
      sizeof(C_T(
       static_cast<C_AT_1>(*static_cast< typename c_std::remove_reference<C_AT_1>::type* >(NULL)) 
      )) 
     >::type* 
    ); 

    template< typename, typename > 
    static int test(...); 

public: 
    static const bool value = (sizeof(test< T, AT_1 >(NULL)) == sizeof(bool)); 
}; 

template< typename T > 
class is_constructible_impl< T, void, void, void, void > 
{ 
private: 
    template< typename C_T > 
    static C_T testFun(C_T); 

    template< typename C_T > 
    static bool test(typename c_std::enable_if< sizeof(C_T) == sizeof(testFun(C_T())) >::type*); 

    template<typename> 
    static int test(...); 

public: 
    static const bool value = (sizeof(test<T>(NULL)) == sizeof(bool)); 
}; 

template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > 
class is_constructible_impl_ptr 
{ 
public: 
    static const bool value = false; 
}; 

template< typename T, typename AT_1 > 
class is_constructible_impl_ptr< T, AT_1, typename enable_if< is_pointer< typename remove_reference<T>::type >::value, void >::type, void, void > 
{ 
private: 
    template< typename C_T > 
    static bool test(C_T); 

    template<typename> 
    static int test(...); 

public: 
    static const bool value = (sizeof(test<T>(static_cast<AT_1>(NULL))) == sizeof(bool)); 
}; 

template< typename T > 
class is_constructible_impl_ptr< T, void, void, void, void > 
{ 
public: 
    static const bool value = true; 
}; 

template< typename T, typename AT_1 = void, typename AT_2 = void, typename AT_3 = void, typename AT_4 = void > 
class is_constructible 
{ 
public: 
    static const bool value = (
     is_pointer< typename remove_reference<T>::type >::value ? 
      is_constructible_impl_ptr< T, AT_1, AT_2, AT_3, AT_4 >::value : 
      is_constructible_impl< T, AT_1, AT_2, AT_3, AT_4 >::value 
    ); 
}; 
+0

хороший, но я все еще получаю жесткую ошибку, когда конструктор является частным [пример] (https://godbolt.org/g/UpV2qb) –

+0

Хммм, это звучит интересно, потому что я тоже проверил этот случай. Какой компилятор вы используете? – Broothy

+0

@Broothy Он дает неправильное значение для конструктивного значения по умолчанию, см. Http://melpon.org/wandbox/permlink/hrTNgb0LrXJyBPnY – Danh

 Смежные вопросы

  • Нет связанных вопросов^_^