c++ conditional compilation based on member presence
The following example demonstrates a technique that can be used (in C++11, makes use of SFINAE for expressions):
typedef char no;
typedef no (&yes)[2];
no detect_push_front( ... );
template < class C >
auto detect_push_front( C &c ) -> typename std::conditional< false, decltype( c.push_front( std::declval< typename C::value_type >() ) ), yes >::type;
template < class C >
struct has_push_front : std::integral_constant< bool, sizeof( detect_push_front( std::declval< C & >() ) ) == sizeof( yes ) > {};
template < class C >
void add( C &c, typename C::value_type v, std::true_type )
{
c.push_front( v );
}
template < class C >
void add( C &c, typename C::value_type v, std::false_type )
{
c.push_back( v );
}
template < class C >
void add( C &c, typename C::value_type v )
{
add( c, v, has_push_front< C >() );
}
int main()
{
std::vector< int > v;
add( v, 0 );
add( v, 1 );
add( v, 2 );
std::copy( v.begin(), v.end(), std::ostream_iterator< int >( std::cout, " " ) );
std::cout << '\n';
std::list< int > l;
add( l, 0 );
add( l, 1 );
add( l, 2 );
std::copy( l.begin(), l.end(), std::ostream_iterator< int >( std::cout, " " ) );
std::cout << '\n';
}
Output:
0 1 2
2 1 0
Conditional code depending on template parameters comparision
As already said, C++17's if constexpr
is the best solution:
template <unsigned long prec1>
DFixed & operator-=(DFixed<prec1> d) {
if constexpr (prec==prec1)
val -= d.val;
else if constexpr (prec<prec1)
val -= d.val/(prec1/prec);
else
val -= d.val*(prec/prec1);
return *this;
}
If you're using C++11, you can use SFINAE with std::enable_if
hidden in a REQUIRES macro:
#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
template <unsigned long prec1, REQUIRES(prec==prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val;
return *this;
}
template <unsigned long prec1, REQUIRES(prec<prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val/(prec1/prec);
return *this;
}
template <unsigned long prec1, REQUIRES(prec>prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val*(prec/prec1);
return *this;
}
Note that input parameter d
is passed by const
reference to avoid unnecessary copy.
Conditional compilation and template
You could place the data in a base class, then use if constexpr
:
template<class T>
struct C_data{
T m_result;
};
template<>
struct C_data<void>{
};
template<class T>
class C: C_data<T>
{
static constexpr auto is_void = std::is_same_v<T,void>;
public:
auto f(){
if constexpr(is_void)
return this->m_result;
else
return;
}
void todo(){
if constexpr(is_void)
this->m_result = doit<T>();
else
doit<T>();
}
};
But it can be argued that a the specialization of the class C is cleaner since all member of a template class should depend on all the template parameter (otherwise you should split your class in order to avoid code bloat).
So I would prefer to fully specialize C, and make part of the class C that are independent of T, a base class of C:
class C_base{
//any thing that is independent of T;
};
template<class T>
class C: public C_base{
//any thing that depend on T
};
template<>
class C<void>: public C_base{
//any thing that depend on T;
};
You could also specialize member funtion by member function, but I find it less clean.
You will find this last code structure in almost all headers of standard library implementations.
Conditional compilation of templates
The standard permits, but does not require, compilers to diagnose templates for which no valid instantiation can be generated. This can range from simple syntax errors to your example of a constant false
expression in a static_assert
. §14.6 [temp.res]/p8:
If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.
I'm rather baffled by all this SFINAE machinery, though. A simple
template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }
already refuses to compile if T
is not constructible from the parameter given, so I'm not sure how this complex circumlocution will help you "avoid null pointers".
Regardless, a simple way to make choosing the second function template a compile-time error is to explicitly delete it.
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;
Alternatively, if you are partial to static_assert
s, perhaps because of the custom error message, you must ensure that there is theoretically a way to generate a valid instantiation of your template. That means that 1) what you are static_assert
ing on must depend on a template argument, and 2) there must be theoretically a way for the condition to be true
. A simple way is to use an auxiliary template:
template<class> class always_false : std::false_type {};
template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( always_false<T>::value, "Class T constructor does not match argument list.");
return nullptr;
}
The key point here is that the compiler cannot assume that always_false<T>::value
is always false
because it is always possible that there's a specialization later that sets it to true
, and so it is not allowed to reject this at template definition time.
Conditional compile based on type
Your issue is that differences in default template arguments don't declare distinct templates. That is why you are getting the redefinition error.
An additional issue is that you need to take the ::value
of the std::is_integral
and std::is_floating_point
traits, as std::enable_if_t
expects a bool
, not a std::integral_constant
.
To fix this, you can use a standard SFINAE trick to declare a template argument of a type which depends on the enable_if_t
result:
template<class T,
class S,
std::enable_if_t<
std::is_floating_point<T>::value && std::is_integral<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(r / static_cast<T>(255),
g / static_cast<T>(255),
b / static_cast<T>(255),
a / static_cast<T>(255));
};
template<class T,
class S,
std::enable_if_t<
std::is_integral<T>::value && std::is_floating_point<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(static_cast<T>(r * 255),
static_cast<T>(g * 255),
static_cast<T>(b * 255),
static_cast<T>(a * 255));
};
Related Topics
Confused by Squaring MACro Sqr in C
C/C++: Casting Away Volatile Considered Harmful
Displacement Map Filter in Opencv
Long VS. Int C/C++ - What's the Point
C++: Timing in Linux (Using Clock()) Is Out of Sync (Due to Openmp)
When Does a Std::Vector Reallocate Its Memory Array
Read Func Interp of a Z3 Array from the Z3 Model
Why Is a C++ Reference Considered Safer Than a Pointer
How Would One Call Std::Forward on All Arguments in a Variadic Function
Is Using an Union in Place of a Cast Well Defined
Why Does Std::Stack Use Std::Deque by Default
Dereferencing One Past the End Pointer to Array Type
Do You Know Tool Building Tree of Include Files in Project\File
How to Make Sure That Std::Random_Shuffle Always Produces a Different Result