如何在构造函数中使用通用引用时允许默认构造? [英] How to allow default construction when using universal reference in constructor?

查看:178
本文介绍了如何在构造函数中使用通用引用时允许默认构造?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(注意:我无法为此找到一个好的标题)



我使用Visual Studio 2013.
我有以下位我的项目中的代码:

  template< class ConditionType> 
class Not
{
ConditionType m_condition;
public:
using this_type = Not< ConditionType> ;;

template< class ... Args
,typename = std :: enable_if<
!std :: is_same< this_type,typename std :: decay< Args> :: type ...> :: value
> :: type
>
Not(Args& ... args)
:m_condition(std :: forward< Args>(args)...){}


Not(const Not&)= default;
不& operator =(const Not&)= default;

template< class ... Args>
bool operator()(Args& ... args)const
{
return! m_condition(std :: forward< Args>(args)...);
}

friend inline bool operator ==(const Not& left,const不& right)
{
return left.m_condition == right.m_condition;
}

friend inline bool operator<(const Not& left,const不& right)
{
return left.m_condition< right.m_condition;
}

};

明显的目的是我有一个ConditionType,它是一个类似的函数对象,得到对应于它的相反的类型。
例如:

  class KeyIsPressed 
{
KeyId m_key;
public:
KeyIsPressed(KeyId key):m_key(std :: move(key)){}

bool operator()(const InputStateBuffer& states)const
{
return states.current()。keyboard()。is_key_pressed(m_key);
}
friend inline bool operator ==(const KeyIsPressed& left,const KeyIsPressed& right)
{
return left.m_key == right.m_key;
}
friends inline bool operator<(const KeyIsPressed& left,const KeyIsPressed& right)
{
return left.m_key< right.m_key;
}
};

void somehere(const InputStateBuffer& input_state_buffer)
{
Not< KeyIsPressed>条件{KEY_SHIFT};

if(condition(input_state_buffer))
do_something();
};

这到目前为止工作得很好,直到我开始使用需要任何构造函数参数。在这种情况下,visual studio编译器触发一个错误:

 错误C2512:'blahblah ::不&blahblah :: SomeConditionType> ;: :not':没有适当的默认构造函数

我试图通过添加一些条件或专业来解决这个问题的Not构造函数,但到目前为止没有什么工作。我没有看到简单的解决方案,但我的enable_if和相关的构造的知识是有限的。



问题的根源是,我需要使用可变参数模板构造函数将构造函数参数传递给实际条件。但是,当没有参数,并且条件可以默认构造,似乎我的代码无法生成一个空的可变参数模板;也许是因为有一个由enable_if生成的最后一个类型。
我在构造函数中使用的enable_if是确保拷贝构造总是调用Not拷贝构造函数,而不是将Not拷贝到条件构造函数。



编辑>

p>这里是一个完整的复制:

  #include< type_traits> 

template< class ConditionType>
class Not
{
ConditionType m_condition;
public:
using this_type = Not< ConditionType> ;;

template< class ... Args
,typename = std :: enable_if<
!std :: is_same< this_type,typename std :: decay< Args> :: type ...> :: value
> :: type
>
Not(Args& ... args)
:m_condition(std :: forward< Args>(args)...){}


Not(const Not&)= default;
不& operator =(const Not&)= default;

template< class ... Args>
bool operator()(Args& ... args)const
{
return! m_condition(std :: forward< Args>(args)...);
}

friend inline bool operator ==(const Not& left,const不& right)
{
return left.m_condition == right.m_condition;
}

friend inline bool operator<(const Not& left,const不& right)
{
return left.m_condition< right.m_condition;
}

};

class ConditionThatCompile
{
int m_key;
public:
ConditionThatCompile(int key):m_key(key){}

bool operator()(const int& value)const
{
return m_key>值;
}
friend inline bool operator ==(const ConditionThatCompile& left,const ConditionThatCompile& right)
{
return left.m_key == right.m_key;
}
friend inline bool operator<(const ConditionThatCompile& left,const ConditionThatCompile& right)
{
return left.m_key< right.m_key;
}
}

class ConditionThatDoNotCompile
{
public:
bool operator()(const int& value)const
{
return true;
}
friend inline bool operator ==(const ConditionThatDoNotCompile& left,const ConditionThatDoNotCompile& right)
{
return true;
}
friend inline bool operator<(const ConditionThatDoNotCompile& left,const ConditionThatDoNotCompile& right)
{
return false;
}
};

void do_Something();

void somehere()
{
不是< ConditionThatCompile> compiling_condition {42};

if(compiling_condition(100))
do_something();

不< ConditionThatDoNotCompile> not_compiling_condition;
};


解决方案

最简单的方法是派生和继承所有构造函数:

  template< typename F> 
struct not:private F
{
using F :: F;

template< typename ... A>
bool operator()(A& ... a)const {return!F :: operator()(std :: forward& }
};

如果你的编译器不支持继承基础构造函数,你需要定义一个构造函数模板,我以前的理解,这个模板看起来确实困惑与隐式定义的复制/移动构造函数。在这种情况下的解决方案是

  template< typename F> 
class Not:F
{
template< typename ... A> struct ok:std :: true_type {};
template< typename ... A> struct ok<不,A ...> :std :: false_type {};

public:
template< typename ... A,typename = typename std :: enable_if<
ok< typename std :: decay< A> :: type ...> {}
> :: type>
不是(A& ... a):F {std :: forward< A>(a)...} {}

template< typename ... A>
bool operator()(A& ... a)const {return!F :: operator()(std :: forward& }
};

这有点笨拙,但是你使用 std :: is_same 不正确。如果这适合你,更好地写出 ok 以更干净的方式包括 decay 给它一个更好的名字。那么它将不那么笨拙。



一个实例表明两种方法都在一些扩展测试中使用。


(note: I'm having trouble finding a good title for this)

I'm using Visual Studio 2013. I have the following bit of code in my project:

template< class ConditionType >
class Not
{
    ConditionType m_condition;
public:
    using this_type = Not<ConditionType>;

    template< class ...Args
        , typename = std::enable_if< 
            !std::is_same< this_type, typename std::decay<Args>::type...>::value 
        >::type
    >
    Not( Args&&... args )
        : m_condition( std::forward<Args>(args)... ) {}


    Not( const Not& ) = default;
    Not& operator=( const Not& ) = default;

    template< class ...Args >
    bool operator()( Args&&... args ) const
    {
        return ! m_condition( std::forward<Args>(args)... );
    }

    friend inline bool operator==( const Not& left, const Not& right )
    {
        return left.m_condition == right.m_condition;
    }

    friend inline bool operator<( const Not& left, const Not& right )
    {
        return left.m_condition < right.m_condition;
    }

};

The obvious purpose is that I have a ConditionType, which is a comparable function object, and I want to get a type which corresponds to it's opposite. For example:

class KeyIsPressed
{
    KeyId m_key;
public:
    KeyIsPressed( KeyId key ) : m_key( std::move(key) ) {}

    bool operator()( const InputStateBuffer& states ) const
    { 
        return states.current().keyboard().is_key_pressed( m_key ); 
    } 
    friend inline bool operator==( const KeyIsPressed& left, const KeyIsPressed& right ) 
    { 
        return left.m_key == right.m_key; 
    } 
    friend inline bool operator<( const KeyIsPressed& left, const KeyIsPressed& right ) 
    { 
        return left.m_key < right.m_key;
    } 
};

void somewhere( const InputStateBuffer& input_state_buffer )
{
    Not<KeyIsPressed> condition { KEY_SHIFT };

    if( condition( input_state_buffer ) ) 
        do_something();
};

This have worked very well so far, until I started using condition types which don't need any constructor parametters. In this case, visual studio compiler triggers an error:

error C2512: 'blahblah::Not<blahblah::SomeConditionType>::Not' : no appropriate default constructor available

I tried to fix this issue by adding some conditions or specializations of the Not constructor, but so far nothing worked. I see no simple solution, but my knowledge of enable_if and related construct is limited.

The source of the problem is that I do need to use variadic templates arguments in the Not constructor to pass the constructor parameters to the actual condition. However, when there is no parametters and the condition can be constructed by default, it seems that my code fails to generate an empty variadic template; maybe because there is a last type generated by the enable_if. The enable_if I used in the constructor is there to make sure that copy construction always call the Not copy constructor instead of forwarding the Not to copy to the condition constructor.

What I can't find is how to allow default construction in Not in this case.

edit>

Here is a full repro-case:

#include <type_traits>

template< class ConditionType >
class Not
{
    ConditionType m_condition;
public:
    using this_type = Not<ConditionType>;

    template< class ...Args
        , typename = std::enable_if< 
            !std::is_same< this_type, typename std::decay<Args>::type...>::value 
        >::type
    >
    Not( Args&&... args )
        : m_condition( std::forward<Args>(args)... ) {}


    Not( const Not& ) = default;
    Not& operator=( const Not& ) = default;

    template< class ...Args >
    bool operator()( Args&&... args ) const
    {
        return ! m_condition( std::forward<Args>(args)... );
    }

    friend inline bool operator==( const Not& left, const Not& right )
    {
        return left.m_condition == right.m_condition;
    }

    friend inline bool operator<( const Not& left, const Not& right )
    {
        return left.m_condition < right.m_condition;
    }

};

class ConditionThatCompile
{
    int m_key;
public:
    ConditionThatCompile( int key ) : m_key( key ) {}

    bool operator()( const int& value ) const
    { 
        return m_key > value;
    } 
    friend inline bool operator==( const ConditionThatCompile& left, const ConditionThatCompile& right ) 
    { 
        return left.m_key == right.m_key; 
    } 
    friend inline bool operator<( const ConditionThatCompile& left, const ConditionThatCompile& right ) 
    { 
        return left.m_key < right.m_key;
    } 
};

class ConditionThatDoNotCompile
{
public:
    bool operator()( const int& value ) const
    { 
        return true;
    } 
    friend inline bool operator==( const ConditionThatDoNotCompile& left, const ConditionThatDoNotCompile& right ) 
    { 
        return true; 
    } 
    friend inline bool operator<( const ConditionThatDoNotCompile& left, const ConditionThatDoNotCompile& right ) 
    { 
        return false;
    } 
};

void do_something();

void somewhere()
{
    Not<ConditionThatCompile> compiling_condition { 42 };

    if( compiling_condition( 100 ) ) 
        do_something();

    Not<ConditionThatDoNotCompile> not_compiling_condition;
};

解决方案

The simplest way to do this is to derive and inherit all constructors:

template<typename F>
struct Not : private F
{
    using F::F;

    template<typename... A>
    bool operator()(A&&... a) const { return !F::operator()(std::forward<A>(a)...); }
};

If your compiler does not support inheriting base constructors, you do need to define a constructor template, and despite my previous understanding, this template appears to get indeed confused with implicitly defined copy/move constructors. A solution in this case is

template<typename F>
class Not : F
{
    template<typename... A> struct ok            : std::true_type {};
    template<typename... A> struct ok<Not, A...> : std::false_type {};

public:
    template<typename... A, typename = typename std::enable_if<
        ok<typename std::decay<A>::type...>{}
    >::type>
    Not(A&&... a) : F{std::forward<A>(a)...} {}

    template<typename... A>
    bool operator()(A&&... a) const { return !F::operator()(std::forward<A>(a)...); }
};

This is a bit clumsy but your use of std::is_same was incorrect. If this works for you, better write ok in a cleaner way including decay outside the class for more general use, and give it a better name. Then it will be less clumsy.

A live example shows that both alternatives work in some extended tests.

这篇关于如何在构造函数中使用通用引用时允许默认构造?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆