Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/156.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在基于策略的类中保持构造的隐含性_C++_C++11_Constructor_Implicit Conversion_Policy Based Design - Fatal编程技术网

C++ 在基于策略的类中保持构造的隐含性

C++ 在基于策略的类中保持构造的隐含性,c++,c++11,constructor,implicit-conversion,policy-based-design,C++,C++11,Constructor,Implicit Conversion,Policy Based Design,考虑一个基于策略的智能指针类Ptr,它只有一个策略可以防止在空状态下(以某种方式)取消引用它。让我们考虑这2种政策: NotNull NoChecking 由于NotNull策略的限制性更大,我们希望允许从Ptr到Ptr的隐式转换,但不允许反向转换。为了安全起见,这一点必须明确。请看一下以下实现: #include <iostream> #include <type_traits> #include <typeinfo> struct NoCheckin

考虑一个基于策略的智能指针类Ptr,它只有一个策略可以防止在空状态下(以某种方式)取消引用它。让我们考虑这2种政策:

  • NotNull
  • NoChecking
由于
NotNull
策略的限制性更大,我们希望允许从
Ptr
Ptr
的隐式转换,但不允许反向转换。为了安全起见,这一点必须明确。请看一下以下实现:

#include <iostream>
#include <type_traits>
#include <typeinfo>

struct NoChecking;
struct NotNull;

struct NoChecking{
  NoChecking()                    = default;
  NoChecking( const NoChecking&)  = default;

  explicit NoChecking( const NotNull& )
  { std::cout << "explicit conversion constructor of NoChecking" << std::endl; }

protected:
  ~NoChecking() {} //defaulting the destructor in GCC 4.8.1 makes it public somehow :o
};

struct NotNull{
  NotNull()                 = default;
  NotNull( const NotNull&)  = default;

  NotNull( const NoChecking& )
  { std::cout << "explicit conversion constructor of NotNull" << std::endl; }

protected:
  ~NotNull()    {}
};

template<
  typename T,
  class safety_policy
> class Ptr
: public safety_policy
{
private:
  T* pointee_;

public:
  template <
    typename f_T,
    class f_safety_policy
  > friend class Ptr;   //we need to access the pointee_ of other policies when converting
                        //so we befriend all specializations of Ptr

    //implicit conversion operator
  template<
    class target_safety
  > operator Ptr<T, target_safety>() const {
    std::cout << "implicit conversion operator of " << typeid( *this ).name() << std::endl;

    static_assert( std::is_convertible<const safety_policy&, const target_safety&>::value,  
                     //What is the condition to check? This requires constructibility
                  "Safety policy of *this is not implicitly convertible to target's safety policy." );

      //calls the explicit conversion constructor of the target type
    return Ptr< T, target_safety >( *this );
  }

    //explicit conversion constructor
  template<
    class target_safety
  > explicit Ptr( const Ptr<T, target_safety>& other )
  : safety_policy( other ),  //this is an explicit constructor call and will call explicit constructors when we make Ptr() constructor implicit!
    pointee_( other.pointee_ )
  { std::cout << "explicit Ptr constructor of " << typeid( *this ).name() << std::endl; }

  Ptr() = default;
};

  //also binds to temporaries from conversion operators
void test_noChecking( const Ptr< int, NoChecking >& )
{  }

void test_notNull( const Ptr< int, NotNull >& )
{  }

int main()
{
  Ptr< int, NotNull    > notNullPtr;                //enforcing not null value not implemented for clarity
  Ptr< int, NoChecking > fastPtr( notNullPtr );     //OK - calling explicit constructor and NotNull is explicitly convertible to NoChecking

  test_notNull    ( fastPtr    );    //should be OK    - NoChecking is implictly convertible to NotNull
  test_noChecking ( notNullPtr );    //should be ERROR - NotNull is explicitly convertible to NoChecking

  return 0;
}
但是,这些错误的可读性不是很强,我们对策略引入了不必要的要求,因此最好回答第一个问题。

请参阅“完美初始化”对于
std::pair
std::tuple
所采取的方法,涉及测试
std::is_constructible::value
std::is_convertible::value

如果两者都为真,则存在隐式转换;如果只有第一个为真,则转换为显式转换


解决方案是为构造函数定义两个重载,一个是隐式的,另一个是显式的,并使用SFINAE,这样最多只能有一个重载是可行的。

我花了一些时间才意识到,但如果问题在于事实,我们不能创建类型为
policy
的对象,因为它的析构函数受
保护
,那么我们为什么不将它托管在一个临时转发类中呢

Ptr隐式转换运算符:

  template<
    class target_safety
  > operator Ptr<T, target_safety>() const {
    std::cout << "implicit conversion operator of " << typeid( *this ).name() << std::endl;

    struct target_host : target_safety { using target_safety::target_safety; };

    static_assert( std::is_convertible<Ptr, target_host>::value,
                     //Now this works, because target_host is constructible!
                  "Safety policy of *this is not implicitly convertible to target's safety policy." );

      //calls the explicit conversion constructor of the target type
    return Ptr< T, target_safety >( *this );
  }

std::is_constructible:value
和反之亦然均为false。由于私有析构函数的存在,不可能构造策略类型的对象,因此我不确定这有什么帮助。不过这是一个迷人的把戏。我肯定我会在别的地方用。
  template<
    class target_safety
  > operator Ptr<T, target_safety>() const {
    std::cout << "implicit conversion operator of " << typeid( *this ).name() << std::endl;

    struct target_host : target_safety { using target_safety::target_safety; };

    static_assert( std::is_convertible<Ptr, target_host>::value,
                     //Now this works, because target_host is constructible!
                  "Safety policy of *this is not implicitly convertible to target's safety policy." );

      //calls the explicit conversion constructor of the target type
    return Ptr< T, target_safety >( *this );
  }
#include <iostream>
#include <type_traits>
#include <typeinfo>

template< typename Policy >
struct policy_host_
: Policy
{
  using Policy::Policy;
};

template< typename Source, typename Target >
struct is_implicitly_convertible
: std::integral_constant<
    bool
  , std::is_constructible< policy_host_<Target>,  policy_host_<Source> >::value &&
    std::is_convertible<   policy_host_<Source>,policy_host_<Target>   >::value
  >
{  };

template< typename Source, typename Target >
struct is_explicitly_convertible
: std::integral_constant<
    bool
  , std::is_constructible< policy_host_<Target>,  policy_host_<Source> >::value &&
    !std::is_convertible<  policy_host_<Source>,policy_host_<Target>   >::value
  >
{  };

struct NoChecking;
struct NotNull;

struct NoChecking{
  NoChecking()                    = default;
  NoChecking( const NoChecking&)  = default;


  explicit NoChecking( const NotNull& )
  { std::cout << "explicit conversion constructor of NoChecking" << std::endl; }

protected:
  ~NoChecking() {} //defaulting the destructor in GCC 4.8.1 makes it public somehow :o
};

struct NotNull{
  NotNull()                 = default;
  NotNull( const NotNull&)  = default;


  NotNull( const NoChecking& )
  { std::cout << "explicit conversion constructor of NotNull" << std::endl; }

protected:
  ~NotNull()    {}
};

template<
  typename T,
  class safety_policy
> class Ptr
: public safety_policy
{
private:
  T* pointee_;

public:
  template <
    typename f_T,
    class f_safety_policy
  > friend class Ptr;   //we need to access the pointee_ of other policies when converting
                        //so we befriend all specializations of Ptr

  template<
    class target_safety,
    typename std::enable_if<
      is_implicitly_convertible< target_safety, safety_policy >::value
    , bool>::type = false
  > Ptr( const Ptr<T, target_safety>& other )
  : safety_policy( other ),
    pointee_( other.pointee_ )
  { std::cout << "implicit Ptr constructor of " << typeid( *this ).name() << std::endl; }

  template<
    class target_safety,
    typename std::enable_if<
      is_explicitly_convertible< target_safety, safety_policy >::value
    , bool>::type = false
  > explicit Ptr( const Ptr<T, target_safety>& other )
  : safety_policy( other ),  //this is an explicit constructor call and will not preserve the implicity of conversion!
    pointee_( other.pointee_ )
  { std::cout << "explicit Ptr constructor of " << typeid( *this ).name() << std::endl; }

  Ptr() = default;
};

  //also binds to temporaries from conversion operators
void test_noChecking( const Ptr< int, NoChecking >& )
{  }

void test_notNull( const Ptr< int, NotNull >& )
{  }

int main()
{
  Ptr< int, NotNull    > notNullPtr;                //enforcing not null value not implemented for clarity
  Ptr< int, NoChecking > fastPtr( notNullPtr );     //OK - calling explicit constructor and NotNull is explicitly convertible to NoChecking

  test_notNull    ( fastPtr    );    //should be OK    - NoChecking is implictly convertible to NotNull
  test_noChecking ( notNullPtr );    //should be ERROR - NotNull is explicitly convertible to NoChecking

  return 0;
}