是SFINAE禁止在模板参数中,还是我遇到了一个clang bug?

时间:2016-02-24 10:46:39

标签: c++ c++11 clang sfinae

以下是我用真实代码点击的问题的简化版本。

简短版:只需查看gcc.godbolt.org /长版本的代码和错误:继续阅读;)

假设我想要一个包含模板参数setting和方法int func(int)的类,例如:

  • settingfalse时,func会返回其参数
  • settingtrue时,func将其论点加倍

最简单的方法是专门化类模板:

template<bool setting> struct A {
    int func(x) const { return 2 * x; }
};
template<> struct A<false> {
    int func(x) { return x; }
};

这种方法的问题在于,如果我有一堆不依赖于setting的其他方法,我将不得不将它们复制粘贴到两个特化中(或从公共基础继承,当没有太多的相互依赖性时。)

相反,我可以使用SFINAE来选择正确的方法,例如与std::enable_if。这要求方法具有模板参数,因为替换失败必须使方法无效,而不是整个类。据我所知,失败可能发生在:

  • 方法的参数类型
  • 方法的返回类型
  • 模板参数类型

以下是使用方法参数的代码:

template<bool setting> struct B {
    template<bool when=true>
    int func(int x
            , typename std::enable_if<when && setting>::type * u=0
            )
    { return 2 * x; }

    template<bool when=true>
    int func(int x
            , typename std::enable_if<when && !setting>::type * u=0
            )
    { return x; }
};

这是使用方法的模板参数的版本:

template<bool setting> struct C {
    template<bool when=true, typename std::enable_if<
              when && setting
            >::type...>
    int func(int x) { return 2 * x; }

    template<bool when=true, typename std::enable_if<
              when && !setting
            >::type...>
    int func(int x) { return x; }
};

我倾向于选择最后一个版本,因为它使方法的签名更具可读性,但这是个人品味的问题。

我的问题涉及到最后一个版本:它是有效的C ++吗? gcc编译得很好,但是clang没有(使用-std=c++11 / c++1y / c++1z进行测试,结果相同)。类定义本身编译好,但是当它被实例化时会发生错误:

int main() {
    A<true> a;
    B<true> b;
    C<true> c;
    return a.func(1) + b.func(2) + c.func(3);
}

在gcc 5.3中编译,但在clang 3.7.1中编译:

test.cpp:30:36: error: call to member function 'func' is ambiguous
                return a.func(1) + b.func(2) + c.func(3);
                                            ~~^~~~
test.cpp:20:7: note: candidate function [with when = true, $1 = <>]
                int func(int x) { return 2 * x; }
                    ^
test.cpp:23:7: note: candidate function [with when = true, $1 = <>]
                int func(int x) { return x; }
                    ^
1 error generated.

这是有效的C ++吗?它是一个铿锵的bug还是gcc在接受这段代码时出错?

1 个答案:

答案 0 :(得分:3)

  

模板参数中是否禁止使用SFINAE

有效。你可能会这样做:

template<bool setting> struct C {
    template<bool when=true, typename std::enable_if<
              when && setting
            >::type* = nullptr>
    int func(int x) { return 2 * x; }

    template<bool when=true, typename std::enable_if<
              when && !setting
            >::type* = nullptr>
    int func(int x) { return x; }
};

Demo

typename std::enable_if<when && !setting>::type...的问题应与CWG 1558有关 所以你的代码在C ++ 17中应该是正确的。