【问题标题】:Using enable_if to hide member function according to template type使用 enable_if 根据模板类型隐藏成员函数
【发布时间】:2021-12-26 17:12:44
【问题描述】:

如果模板参数匹配特定类型,我希望我的类模板提供额外的函数成员。我正在尝试使用 std::enable_if 来获得 SFINAE 实现,但我正在努力为此找到正确的语法。对于类似的问题,我尝试了几种解决方案,但由于某种原因,它们都无法编译。

#include <type_traits>
#include <string>

template < typename T >
class myClass {
 public:
    using value_type = T;
    using other_type = int;

    myClass() = default;
    virtual ~myClass() = default;

    // 'default' member
    void function(const value_type& val) {};

    // overload #1
    // error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
    template < typename = typename std::enable_if< !std::is_convertible< other_type, value_type >::value >::type >
    void function(const other_type& val) {};

    // overload #2
    // error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
    template < std::enable_if_t< !std::is_convertible< other_type, value_type >::value >* = nullptr >
    void function(const other_type& val) {};
};



int main(int argc, char const *argv[]) {
    myClass< std::string > foo;  // OK
    myClass< float > bar;            // error: no type named ‘type’ in ‘struct std::enable_if<false, void>’

    return 0;
}

我希望function 可用于所有类型,但它的重载other_type 不能隐式转换为value_type 时可用。我将如何实施?

【问题讨论】:

  • 您可以访问 C++20 吗? requires(!std::is_convertible&lt;other_type, value_type &gt;::value) 会解决这个问题。

标签: c++ templates sfinae typetraits


【解决方案1】:

要应用 SFINAE,您需要尝试使用 SFINAE 的对象拥有自己的模板参数,并且您需要在条件中使用该模板参数。在这种情况下,您可以通过将模板参数typename V = value_type 添加到模板参数并使用V 而不是value_type 来修复它。现在条件将取决于V,它是成员函数的模板参数:

#include <type_traits>
#include <string>

template < typename T >
class myClass {
 public:
    using value_type = T;
    using other_type = int;

    myClass() = default;
    virtual ~myClass() = default;

    // 'default' member
    void function(const value_type& val) {};

    // overload #1
    // error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
    template < typename V = value_type, typename = typename std::enable_if< !std::is_convertible< other_type, V >::value >::type >
    //   added ^^^^^^^^^^^^^^^^^^^^^^^^                                               changed value_type to V ^
    void function(const other_type& val) {};

    // overload #2
    // error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
    template < typename V = value_type, std::enable_if_t< !std::is_convertible< other_type, V >::value >* = nullptr >
    //   added ^^^^^^^^^^^^^^^^^^^^^^^^                             changed value_type to V ^
    void function(const other_type& val) {};
};



int main(int argc, char const *argv[]) {
    myClass< std::string > foo;  // OK
    myClass< float > bar;        // OK

    return 0;
}

【讨论】:

    【解决方案2】:

    function 成员函数模板重载中,SFINAE 构造 (std::enable_if_t) 不包含依赖类型,这意味着没有替换上下文,并且模板头中的重载失败不是替代失败,它们是硬错误。回忆:

    • SFINAE - 替代失败不是错误

    您通常通过添加一个虚拟模板参数来解决此问题,其默认模板参数是您最初直接使用的封闭类模板的模板参数(T;您的值类型):

    template <typename U = T, typename = typename std::enable_if<
                                  !std::is_convertible<other_type, U>::value>::type>
    void function(const other_type &val){};
    
    template <
        typename U = T,
        std::enable_if_t<!std::is_convertible<other_type, U>::value> * = nullptr>
    void function(const other_type &val){};
    

    std::enable_if_t SFINAE 构造现在依赖于每个成员函数模板重载的上下文。

    【讨论】:

    • 是的,但是为这个特定目的使用std::enable_if 的正确语法是什么(我假设这是可能的?)
    • @joaocandre 查看更新后的答案(我在你问你问题的同时更新了它:))。
    • 谢谢,这正是我想要的。
    【解决方案3】:

    自从在 C++20 中引入概念以来,大多数 SFINAE 机器 可以简化或至少表达得不那么冗长。

    #include <concepts>
    #include <iostream>
    #include <string>
    
    template < class Type >
    class myClass {
     public:
        myClass() = default;
    
        void function(Type const& val) {
            std::cout << "Default: " << val << "\n";
        }
    
        template < class Other >
            requires (not std::convertible_to<Other, Type>)  // <- Constrain
        void function(Other const& val) {
            std::cout << "Overload: " << val << "\n";
        }
    };
    
    
    
    int main()
    {
        myClass< std::string > foo;
        foo.function("OK");          // Default
        foo.function(42);            // Overload
    
        myClass< float > bar;
        bar.function(42);            // Default
        bar.function("OK");          // Overload
        bar.function(3.14);          // Default
    }
    

    【讨论】:

    • 对于像我这样想要允许/禁止不以 Other 作为参数的函数的人来说,您仍然可以使用上面的 tric,通过使用默认值定义模板模板参数:``` template 需要(某些条件)void function(); // 仅在某些条件为真时才实例化 ```
    猜你喜欢
    • 1970-01-01
    • 2012-01-22
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-07
    • 1970-01-01
    • 2017-05-09
    相关资源
    最近更新 更多