【问题标题】:why template parameter which is explicitely given can not be "deduced"为什么不能“推导出”显式给出的模板参数
【发布时间】:2020-11-19 12:37:24
【问题描述】:

来自那个问题: Using enum values in combination with SFINAE

我尝试实现:

enum Specifier
{
    One,
    Two,
    Three
};

template <Specifier, typename UNUSED=void>
struct Foo 
{
        void Bar(){ std::cout << "Bar default" << std::endl;}
};

template <Specifier s , typename std::enable_if<s == Specifier::Two || s == Specifier::One, int>::type>
struct Foo<s>
{
    void Bar(){ std::cout << "Bar Two" << std::endl; }
};


int main()
{
   Foo< One >().Bar();
   Foo< Two >().Bar();
}

失败:

> main.cpp:130:8: error: template parameters not deducible in partial specialization:
  130 | struct Foo<s>
      |        ^~~~~~
   main.cpp:130:8: note:         '<anonymous>'

如何解决这个超级简单的例子?我喜欢 SFINAE :-)

【问题讨论】:

  • 您的代码看起来与您链接的问题的已接受答案中的代码不同
  • @idclev463035818:是的,因为这是我尝试解决的问题。我不明白为什么!
  • 也许可以在问题中澄清这一点。我现在明白这是试图回答这个问题,而不是试图应用答案,对吧?

标签: c++ templates sfinae template-specialization


【解决方案1】:

enable_if 放入Foo 的模板参数列表中:

template <Specifier s>
struct Foo<s, typename std::enable_if<s == Specifier::Two || s == Specifier::One, void>::type>
//                                           same as the default type used before ^^^^

demo.

【讨论】:

  • @dfrib 当然可以,但我觉得有必要强调它必须与Foo 的第一个定义中的默认参数相同,否则它不会按预期工作。跨度>
【解决方案2】:

正如错误告诉我们的那样,模板参数在偏特化中是不可推导出的。在您的示例中,您尝试将 SFINAE 构造放在专业化的模板 parameter 列表中,但您需要将其移动到模板 argument 列表中(该类是专业化声明的)。

template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>>

应用于您的示例(稍微清理一下):

#include <iostream>
#include <type_traits>

enum class Specifier {
    One,
    Two,
    Three
};

template <Specifier, typename = void>
struct Foo {
    static void bar() { std::cout << "bar default\n"; }
};

template <Specifier S>
struct Foo<S, std::enable_if_t<(S == Specifier::Two) || (S == Specifier::One)>> {
    static void bar() { std::cout << "bar One or Two\n"; }
};

int main() {
    Foo<Specifier::One>::bar();    // bar One or Two
    Foo<Specifier::Two>::bar();    // bar One or Two
    Foo<Specifier::Three>::bar();  // bar default
}

注意,类模板Foo的主模板中不用的类型模板参数不用命名。

【讨论】:

    【解决方案3】:

    模板特化中的模板参数是不可推导出的,这是一个简单的事实。

    不是你需要它。

    只需更改模板专业化:

    template <Specifier s>
    struct Foo<s, std::enable_if_t<s == Specifier::Two || s == Specifier::One, int>>
    

    当然,std::enable_if_t 的结果是int 而不是void,这让它有点没用。

    另外,正如其他人评论的那样,使用概念或至少 requires 代替主模板的额外模板参数要方便得多。

    【讨论】:

    • 编译,但从给定的 main 调用时没有选择正确的专业化。它总是打印“Bar default”
    • 我想我在你的评论之前添加了一个解释,并在之后添加了更多......
    • godbolt.org/z/86MbfY 不起作用。将int 更改为void
    • @JHBonarius:是的,这就是问题所在 :-) 谢谢
    • “概念”是更好的方法,更容易阅读,这很清楚,但我只是想要一些“旧的 SFINAE 痛苦”:-) 仅用于我的个人教育......
    猜你喜欢
    • 2018-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-21
    • 2011-08-28
    • 2017-05-20
    相关资源
    最近更新 更多