SFINAE для выбора конструктора на основе параметра шаблона значения класса - PullRequest
0 голосов
/ 01 мая 2020

Я пытаюсь написать класс, который предоставляет различные конструкторы в зависимости от значения собственных параметров шаблона класса. Наивный код, который пришёл в голову при попытке сделать это, выглядит следующим образом:

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

Компиляция класса без какого-либо его экземпляра работает нормально, но попытка его создания каким-либо образом или емкостью всегда терпит неудачу. Ошибка компиляции всегда имеет один и тот же формат и сообщается для каждого конструктора, где SFINAE должен сработать:

error: no type named ‘type’ in ‘struct std::enable_if’

Видимо std::enable_if работает как задумано, но почему-то не должно считать ошибкой. Любая подсказка о том, что это все о?

Ответы [ 3 ]

1 голос
/ 01 мая 2020

Чтобы использовать SFINAE, параметры шаблона должны быть частью текущего шаблона. Поскольку compile_time_w и compile_time_h являются частью параметров шаблона класса, их нельзя использовать. Чтобы исправить это, добавьте их в каждый шаблон функции, например

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;
}
1 голос
/ 01 мая 2020

И версия C ++ 20:

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;
};
1 голос
/ 01 мая 2020

SFINAE работает с параметрами шаблона функции-шаблона; вы должны создавать шаблоны-конструкторы, имеющие собственные параметры шаблона, и проверять их с помощью std::enable_if вместо параметра шаблона класса; в противном случае при определенной реализации шаблона класса будут созданы все шаблоны конструктора, что приведет к ошибке.

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) {}

LIVE

...