在尝试使用 SFINAE 禁用功能时,我是否创建了未定义的行为?

While trying to use SFINAE to disable functions, have I created undefined behavior?

我正在尝试使用 SFINAE 根据一些非模板枚举参数禁用 class 的某些功能。

以下代码不能使用 gcc 编译,但在使用 msvc 编译器时似乎可以按预期编译和工作。

#include <iostream>
#include <type_traits>

enum class B { VARIANT1, VARIANT2 };

template<B B_VAL>
struct A {
    template<class = std::enable_if_t<B_VAL == B::VARIANT1>>
    void func1() {
        std::cout<<"VARIANT1"<<std::endl;
    }
    template<class = std::enable_if_t<B_VAL == B::VARIANT2>>
    void func2() {
        std::cout<<"VARIANT2"<<std::endl;
    }
};

int main()
{
    A<B::VARIANT1> a;
    a.func1();
}

预期的(和 msvcs)行为​​是调用 enable_if_t 条件等于 false 的函数会导致编译时错误,或者如果存在重载函数,则会删除用于重载解析的候选函数在这个例子中。在所有其他情况下,代码应该可以正常编译。

另一方面,

gcc 告诉我它在 func2 模板中的 enable_if_t 的“struct std::enable_if”中找不到名为“type”的类型,这非常有意义,因为名为“type”的成员仅在条件等于 true 时出现在 enable_if 中。但这不应该是 SFINAE 功能所需的行为吗?编译器不应该忽略 func2,因为它从未被调用过吗?

我现在有三个问题:

  1. 由于两个编译器产生不同的行为,是否未定义,如果是,parts/statements?

  2. SFINAE 是否适合实现我的目标,还是我误解了它的用例?

  3. 使用静态断言作为替代方案会更好吗?

如果这个问题与 重复,我很抱歉,但我认为那里的答案对我的问题没有太大帮助。

GCC 是对的。这是因为您没有将 SFINAE 用于您的功能。看起来你这样做是因为你使用了标准库中的 SFINAE 实用程序,但这里缺少一个关键成分。

“SFINAE”中的'S'代表替换。将模板参数替换为我们尝试实例化的模板的参数。现在,有问题的模板是 func2。为了使 SFINAE 工作,func2 的参数必须不能被其参数替换。但是这里

std::enable_if_t<B_VAL == B::VARIANT2>

func2参数没有被使用。它不依赖于替换为 func2 期间发生的任何事情。它只是一个无效类型,完全独立于实际实例化 func2.

的尝试

虽然不难修复

template<B B_VAL_ = B_VAL, class = std::enable_if_t<B_VAL_ == B::VARIANT1>>
void func1() {
    std::cout<<"VARIANT1"<<std::endl;
}

template<B B_VAL_ = B_VAL, class = std::enable_if_t<B_VAL_ == B::VARIANT2>>
void func2() {
    std::cout<<"VARIANT2"<<std::endl;
}

现在,检查是针对正确模板的替换。