I have a quite complex piece of code which I simplified to this reproducer :
#include <type_traits>
#include <tuple>
template<typename ...As>
struct outer {
template<typename ...Bs>
struct inner {
template<bool dummy, typename E = void>
struct problem;
using TA = std::tuple<As...>;
using TB = std::tuple<Bs...>;
template<bool dummy>
struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value < std::tuple_size<TB>::value>::type>
{
static constexpr auto val() { return 1; } // actually a complex function
};
template<bool dummy>
struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value >= std::tuple_size<TB>::value>::type>
{
static constexpr auto val() { return 0; }
};
};
};
int main() {
return outer<int, float>::inner<double>::problem<false>::val();
}
it doesn't compile (with gcc or clang), saying :
<source>:13:82: error: failed requirement 'std::tuple_size<std::tuple<int, float> >::value < std::tuple_size<std::tuple<double> >::value'; 'enable_if' cannot be used to disable this declaration
struct problem<dummy, typename std::enable_if<std::tuple_size<TA>::value <std::tuple_size<TB>::value>::type>
~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:27:31: note: in instantiation of template class 'outer<int, float>::inner<double>' requested here
return outer<int, float>::inner<double>::problem<false>::val();
I tried some variants, and nothing works.
I read some already posted Q&A, such as : this one or this one but they don't seem to answer my question.
PS : I can use C++17, but that must work with any compiler.
Suggestion: try something as follows
struct inner {
using TA = std::tuple<As...>;
using TB = std::tuple<Bs...>;
template<bool dummy, typename UA = TA, typename E = void>
struct problem;
template<bool dummy, typename UA>
struct problem<dummy, UA,
std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
{ static constexpr auto val() { return 1; } };
template<bool dummy, typename UA>
struct problem<dummy, UA,
std::enable_if_t<(std::tuple_size_v<UA> >= std::tuple_size_v<TB>)>>
{ static constexpr auto val() { return 0; } };
};
I mean... take in count that SFINAE works with tests over template parameter of the struct/class (or function, or method) that you want enable/disable.
The problem in your original code is that the SFINAE test regards only TA
and TB
that are types defined in the inner
struct that contain problem
. So the test depends only from external template parameters (As...
and Bs...
), not from template arguments of problem
.
Adding a template argument with default value for problem
// ..................VVVVVVVVVVVVVVVVV
template<bool dummy, typename UA = TA, typename E = void>
struct problem;
doesn't change the practical use of problem
itself but transform the test in std::enable_if
template<bool dummy, typename UA>
struct problem<dummy, UA, // ..........VV UA, not TA
std::enable_if_t<(std::tuple_size_v<UA> < std::tuple_size_v<TB>)>>
{ static constexpr auto val() { return 1; } };
in a test that involve a template parameter of problem
itself.