Reputation: 642
I'm trying to write a class that exposes different constructors depending on the value of the class's own template parameters. The naive code that came to mind trying to do this is as follows :
// C++14
#include <type_traits>
template <int compile_time_w = -1, int compile_time_h = -1>
struct Grid
{
template <std::enable_if_t<compile_time_w < 0 && compile_time_h < 0, int> = 0>
Grid(int runtime_w, int runtime_h) : _w(runtime_w), _h(runtime_h) {}
template <std::enable_if_t<compile_time_w < 0 && compile_time_h >= 0, int> = 0>
Grid(int runtime_w) : _w(runtime_w), _h(compile_time_h) {}
template <std::enable_if_t<compile_time_w >= 0 && compile_time_h < 0, int> = 0>
Grid(int runtime_h) : _w(compile_time_w), _h(runtime_h) {}
template <std::enable_if_t<compile_time_w >= 0 && compile_time_h >= 0, int> = 0>
Grid() : _w(compile_time_w), _h(compile_time_h) {}
int _w, _h;
};
int main()
{
// Grid<2, 2> grid; // any combination of template parameters + constructor parameters fails to compile
return 0;
}
Compiling the class without any instantiation of it works fine, but trying to instantiate it in any way or capacity always fails. The compilation error is always of the same format, and is reported for every constructor where SFINAE should trigger :
error: no type named ‘type’ in ‘struct std::enable_if’
Apparently std::enable_if
is working as intended, but somehow what should not be considered as an error is. Any clue on what this is all about ?
Upvotes: 0
Views: 339
Reputation: 75853
And the C++20 version:
template <int compile_time_w = -1, int compile_time_h = -1>
struct Grid
{
Grid(int runtime_w, int runtime_h) requires (compile_time_w < 0 && compile_time_h < 0)
: _w(runtime_w), _h(runtime_h) {}
Grid(int runtime_w) requires(compile_time_w < 0 && compile_time_h >= 0)
: _w(runtime_w), _h(compile_time_h) {}
Grid(int runtime_h) requires(compile_time_w >= 0 && compile_time_h < 0)
: _w(compile_time_w), _h(runtime_h) {}
Grid() requires(compile_time_w >= 0 && compile_time_h >= 0)
: _w(compile_time_w), _h(compile_time_h) {}
int _w, _h;
};
Upvotes: 1
Reputation: 180955
In order to use SFINAE, the template parameters must be part of the current template. Since compile_time_w
and compile_time_h
are part of the class's template parameters, they are not usable. To fix it add them to each function template like
template <int compile_time_w = -1, int compile_time_h = -1>
struct Grid
{
template <int compile_time_w_l = compile_time_w, int compile_time_h_l = compile_time_h, std::enable_if_t<compile_time_w_l < 0 && compile_time_w_l < 0, int> = 0>
Grid(int runtime_w, int runtime_h) : _w(runtime_w), _h(runtime_h) {}
template <int compile_time_w_l = compile_time_w, int compile_time_h_l = compile_time_h, std::enable_if_t<compile_time_w_l < 0 && compile_time_w_l >= 0, int> = 0>
Grid(int runtime_w) : _w(runtime_w), _h(compile_time_h) {}
template <int compile_time_w_l = compile_time_w, int compile_time_h_l = compile_time_h, std::enable_if_t<compile_time_w_l >= 0 && compile_time_w_l < 0, int> = 0>
Grid(int runtime_h) : _w(compile_time_w), _h(runtime_h) {}
template <int compile_time_w_l = compile_time_w, int compile_time_h_l = compile_time_h, std::enable_if_t<compile_time_w_l >= 0 && compile_time_w_l >= 0, int> = 0>
Grid() : _w(compile_time_w), _h(compile_time_h) {}
int _w, _h;
};
int main()
{
Grid<2, 2> grid; // any combination of template parameters + constructor parameters fails to compile
return 0;
}
Upvotes: 1
Reputation: 172994
SFINAE works with template parameters of function template; you should make constructor templates owning their own template parameters, and check them with std::enable_if
instead of class template parameter; otherwise, with certain class template instantiation all the constructor templates would be instantiated and cause the error.
template <int w = compile_time_w, int h = compile_time_h, std::enable_if_t<w < 0 && h < 0, int> = 0>
Grid(int runtime_w, int runtime_h) : _w(runtime_w), _h(runtime_h) {}
template <int w = compile_time_w, int h = compile_time_h, std::enable_if_t<w < 0 && h >= 0, int> = 0>
Grid(int runtime_w) : _w(runtime_w), _h(compile_time_h) {}
template <int w = compile_time_w, int h = compile_time_h, std::enable_if_t<w >= 0 && h < 0, int> = 0>
Grid(int runtime_h) : _w(compile_time_w), _h(runtime_h) {}
template <int w = compile_time_w, int h = compile_time_h, std::enable_if_t<w >= 0 && h >= 0, int> = 0>
Grid() : _w(compile_time_w), _h(compile_time_h) {}
Upvotes: 1