I want my class use another implementation for types don't have constexpr constructor.
like this:
template <typename A>
class foo
{
public:
// if A has constexpr constructor
constexpr foo() :_flag(true) { _data._a = A(); }
// else
constexpr foo() : _flag(false) { _data.x = 0; }
~foo(){}
bool _flag;
union _data_t
{
_data_t() {} // nothing, because it's just an example
~_data_t() {}
A _a;
int x;
}_data;
};
To achieve what the title says, I try this:
template<typename _t, _t = _t()>
constexpr bool f()
{
return true;
}
template<typename _t>
constexpr bool f()
{
return false;
}
It works well for types haven't constexpr constructor. But for other types it causes a compile error with ambiguous overloads.
so how can I check?
CodePudding user response:
I suppose you can use SFINAE together with the power of the comma operator
Following your idea, you can rewrite your f()
functions as follows
template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }
template <typename>
constexpr bool f (long)
{ return false; }
Observe the trick: int = (T{}, 0)
for the second template argument
This way f()
is enabled (power of the comma operator) only if T{}
can be constexpr constructed (because (T{}, 0)
is the argument for a template parameter), otherwise SFINAE wipe away the first version of f()
.
And observe that the fist version of f()
receive an unused int
where the second one receive a long
. This way the first version is preferred, when available, calling f()
with an int
; the second one is selected, as better than nothing solution, when the first one is unavailable (when the first template argument isn't constexpr default constructible).
Now you can construct two template constructors for foo
that you can alternatively enable/disable according the fact the template parameter T
(defaulted to A
) is or isn't constexpr constructible
template <typename T = A,
std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }
template <typename T = A,
std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }
The following is a full compiling example (C 14 or newer, but you can modify it for C 11):
#include <iostream>
#include <type_traits>
template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }
template <typename>
constexpr bool f (long)
{ return false; }
template <typename A>
struct foo
{
template <typename T = A,
std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }
template <typename T = A,
std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }
};
struct X1 { constexpr X1 () {} };
struct X2 { X2 () {} };
int main()
{
foo<X1> f1; // print "constexpr"
foo<X2> f2; // print "not constexpr"
}