【问题标题】:SFINAE to select constructor based on class value template parameterSFINAE 根据类值模板参数选择构造函数
【发布时间】:2020-05-01 14:22:13
【问题描述】:

我正在尝试编写一个根据类自身模板参数的值公开不同构造函数的类。尝试执行此操作时想到的幼稚代码如下:

// 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 正在按预期工作,但不知何故不应将其视为错误。关于这一切的任何线索?

【问题讨论】:

  • 类模板参数不是模板构造函数的直接上下文的一部分,因此 SFINAE 不适用。
  • 解释下提到了here

标签: c++ templates sfinae


【解决方案1】:

为了使用 SFINAE,模板参数必须是当前模板的一部分。由于compile_time_wcompile_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;
}

【讨论】:

    【解决方案2】:

    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

    【讨论】:

    • SFINAE 使用函数模板的模板参数 不是。您也可以 SFINAE 类模板。只是所有参数需要在同一个模板中。
    • @NathanOliver SFINAE 不是用于重载解析吗? 此规则适用于函数模板的重载解析:
    • 它是,但它的用途不止于此。例如,您可以将部分专业化与其结合以获得“通用专业化”:stackoverflow.com/a/60399429/4342498
    【解决方案3】:

    还有 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;
    };
    

    【讨论】:

    • 虽然我知道您可能无法使用它,因为您特别要求 C++14,但它可以在未来帮助他人或您自己。
    • 我正在制作自己的框架,所以我真的可以。我只是不确定对谁能够使用它有什么影响。
    • @Matrefeytontias 对概念的支持在 gcc 和 clang 的主干中。用户何时切换到 C++20 是另一回事。
    猜你喜欢
    • 2017-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-13
    • 2013-01-14
    • 1970-01-01
    • 2017-01-14
    相关资源
    最近更新 更多