2016-11-05 111 views
7

我的愛好庫的基本組件必須與C++ 98和C++ 11編譯器配合使用。爲了學習和享受我自己,我創建了幾種類型支持功能(如enable_if,conditional,is_same,is_integral等)的C++ 98實現,以便在沒有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

它不使用g ++ 4.3.6

+2

您的版本隱式使用表達式SFINAE。儘管C++ 03似乎沒有(明確地)禁止它,但很多實現在幾天內都不支持它。所以不是一個令人滿意的解 – Columbo

+0

@Columbo它使用g ++ 4.4.7,但是,它不適用於g ++ 4.3.6 – Danh

+0

如果構造函數是私有的,該怎麼辦? –

4

我覺得DANH的想法是偉大的工作!只需稍作修改,我們就可以消除新的操作員。 (我有一個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