跨编译器的std :: enable_if的不同行为(取决于外部类模板参数)

时间:2019-01-03 18:02:43

标签: c++ c++11 templates gcc enable-if

我有一个嵌套(Inner)类,我想为其构建一个enable_if的构造函数,具体取决于封闭类({{1} })。

我想出了下面的代码,只是发现它在某些编译器上可以正常编译,而在某些编译器上则可以。

Args

在Godbolt上显示:https://godbolt.org/z/lsivO9

有趣的部分是结果:

  • Outer-无法编译
  • #include <tuple> #include <type_traits> template <typename... Args> struct Outer { struct Inner { Inner(const Outer* out, Args... vals) : outer(out) , values(vals...) {} // This ctor should be enabled only if Args are non-empty template <typename = std::enable_if_t<(sizeof...(Args) > 0)>> Inner(const Outer* out) : outer(out) {} const Outer* outer; std::tuple<Args...> values; }; }; int main() { Outer<int, int> o1; Outer<int, int>::Inner i1(&o1, 1, 2); Outer<int, int>::Inner i11(&o1); Outer<> o2; Outer<>::Inner i2(&o2); Outer<>::Inner i21(nullptr); } -确定
  • GCC 8.2 -std=c++17-确定
  • GCC trunk -std=c++17-确定
  • MSVC 19.14 /std:c++17 /permissive--无法编译
  • MSVC 19.16 /std:c++17 /permissive--无法编译

那么,这些问题:

  • clang 7 -std=c++17类的clang trunk -std=c++17是否在Args...类的直接上下文中?
  • 以上示例格式正确吗?
  • 哪个编译器正确?
  • 为什么GCC在树干上表现出不同?

2 个答案:

答案 0 :(得分:3)

您需要使std::enable_if依赖于构造函数模板参数

template <std::size_t N = sizeof...(Args), std::enable_if_t<(N > 0), bool> = true>
Inner(const Outer* out)
: outer(out)
{}

答案 1 :(得分:1)

template <typename = std::enable_if_t<(sizeof...(Args) > 0)>>
     Inner(const Outer* out)

Args为空时,此为

template <typename = std::enable_if_t<false>>
     Inner(const Outer* out)

SFINAE仅在SFINAE依赖于函数的模板参数时才适用于函数。这里不是。因此,硬错误是正确的。

这可能是不需要诊断且程序仍然格式错误的情况(因此,编译器可以随意执行任何操作)。解决这个问题很棘手,而且由于有一个简单的解决方法,因此您最好也这样做。

template <std::size_t N = sizeof...(Args), typename = std::enable_if_t<(N > 0)>>

或我的首选

template <std::size_t N = sizeof...(Args), std::enable_if_t<(N > 0), bool> = true>

在过载时效果更好。

但是,在您的特定情况下,第一个ctor变成了第二个:

    Inner(const Outer* out, Args... vals) 
        : outer(out)
        , values(vals...)
     {}

Args...为空时,所以这里看不到重点。