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

时间:2019-06-13 23:07:19

标签: c++ c++14

为什么在这个简单的示例中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等)的情况下使该代码正常工作?

2 个答案:

答案 0 :(得分:1)

来自cppreference

  

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

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

这些概念旨在帮助减轻这种繁琐的工作量,因此,如果编译器已经支持它们,则可以尝试使用它们。

答案 1 :(得分:1)

如果您将第一个功能更改为:

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)是可以接受的匹配。