为什么 SFINAE 在这个简单的成员函数重载中不起作用



为什么SFINAE在这个简单的例子中不起作用?如果我注释掉模板化的"添加",代码编译正常。为什么编译器在替换失败后不尝试调用非模板"add"?

我正在使用 MSVS 2017。


#include <set>
#include <memory>
struct button_t
{
    virtual ~button_t() {}
};
struct up_down_button_t : button_t
{
};

struct gui_t
{
    std::set<std::shared_ptr<button_t> > buttons;
    void add(const std::shared_ptr<button_t>& b) {
        buttons.insert(b);
    }
    template<class container_t>
    void add(container_t& c) {
        for (auto& i : c)
            add(i);
    }
} gui;
int main(int argc, char* argv[]) {

    auto b = std::make_shared<up_down_button_t>();
    gui.add(b);
}

是否有可能使该代码在没有冗长的样板代码(如 std::enable_if 等(的情况下工作?

来自 cpp首选项:

只有函数类型或其模板参数类型 [或其显式说明符(自 C++20 起(] 的直接上下文中的类型和表达式中的失败才是 SFINAE 错误

在这里,故障发生在函数体中,因此它是替换失败,但不是在 SFINAE 上下文中 - 因此它是一个错误。

概念旨在帮助减少样板的繁重,因此如果您的编译器已经支持它们,您可以尝试使用它们。

如果将

第一个函数更改为:

template <class ptr_t>
void add(std::shared_ptr<ptr_t>& b) {
    buttons.insert(b);
}

然后,您的代码将按预期编译并工作。

需要注意的两件事:

  • 这两个功能都是template的。
  • 两者都引用非const对象。
有效的模板函数

比需要一些"转换"的非模板函数更可取。

顺便说一下,您的示例中没有 SFINAE。事实上,template<class container_t> void add(container_t& c)是可以接受的匹配。

最新更新