(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;
};