简体   繁体   English

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

[英]Is SFINAE forbidden in template arguments, or did I hit a clang bug?

What follows is a simplified version of a problem I hit with real code. 以下是我用实际代码命中的问题的简化版本。

Short version : just look at the code and error at gcc.godbolt.org / long version: read on ;) 简短版本 :只需查看gcc.godbolt.org / long版本中的代码和错误:继续阅读;)

Suppose I want a class with a template parameter setting and a method int func(int) such as: 假设我想要一个带有模板参数setting的类和一个方法int func(int)例如:

  • when setting is false , func returns its argument settingfalsefunc返回其参数
  • when setting is true , func doubles its argument settingtruefunc其参数加倍

The simplest way to do that is to specialize the class template: 最简单的方法是专门化类模板:

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

The problem with this approach is that if I have a bunch of other methods that don't depend on setting , I'll have to copy-paste them in both specialization (or inherit from a common base, when there's not too much inter-dependencies). 这种方法的问题在于,如果我有一堆不依赖于setting的其他方法,我将不得不将它们复制粘贴到两个特化中(或者从一个公共基础继承,当没有太多的依赖)。

So instead, I can use SFINAE to select the right method, eg with std::enable_if . 因此,我可以使用SFINAE选择正确的方法,例如使用std::enable_if This requires the method to have a template argument, because substitution failure must invalidate just the method, not the whole class. 这要求方法具有模板参数,因为替换失败必须使方法无效,而不是整个类。 As far as I know, the failure can occur in either of: 据我所知,失败可能发生在以下任何一个:

  • a method's argument type 方法的参数类型
  • the method's return type 方法的返回类型
  • a template parameter type 模板参数类型

Here's the code using the method's arguments: 这是使用方法参数的代码:

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

And here's the version using the method's template arguments: 这是使用方法的模板参数的版本:

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

I tend to prefer the last version, as it makes the method's signature more readable, but that's a matter of personal taste. 我倾向于选择最后一个版本,因为它使方法的签名更具可读性,但这是个人品味的问题。

My question concerns this last version: is it valid C++ ? 我的问题涉及到最后一个版本:它是有效的C ++吗? gcc compiles it fine, but clang does not (tested with -std=c++11 / c++1y / c++1z with same results). gcc编译得很好,但是clang没有(用-std=c++11 / c++1y / c++1z ,结果相同)。 The class definition in itself compiles OK, but the error occurs when it's instantiated: 类定义本身编译好,但是在实例化时会发生错误:

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

compiles in gcc 5.3 but not with clang 3.7.1: 在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.

So is this valid C++ ? 这个有效的C ++也是如此? Is it a clang bug or is gcc wrong in accepting this code ? 它是一个铿锵的bug还是gcc在接受这段代码时出错了?

Is SFINAE forbidden in template arguments 模板参数中是否禁止使用SFINAE

It is valid. 这是有效的。 You may do for example: 你可能会这样做:

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 演示

The problem with typename std::enable_if<when && !setting>::type... should be related to CWG 1558 . typename std::enable_if<when && !setting>::type...应该与CWG 1558有关
And so your code should be correct in C++17. 所以你的代码在C ++ 17中应该是正确的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM