【问题标题】:C++ template argument limited to classes (not basic types)C++ 模板参数仅限于类(不是基本类型)
【发布时间】:2022-03-02 03:14:21
【问题描述】:

是否可以指定一个模板参数,它永远不会匹配基本类型,例如 int?我正在与歧义作斗争。比如:

template<class T> void Function(const T& x) { SetString(x.GetString()); };

这只有在 T 中有 GetString 方法时才有效,但如果编译器看到这个函数,它会尝试使用它,即使 T 只是 int。

【问题讨论】:

  • 我认为std::enable_if 是您正在寻找的。​​span>
  • 如果有人试图写Function(35);,你想发生什么?在int 案例中,您还想让它做什么?
  • std::enable_ifstd::is_fundamentalstd::is_arithmetic 结合使用
  • 不幸的是,C++20 不是一个选项。概念将通过{ t.GetString() } -&gt; std::convertable_to&lt;std::string&gt; 的要求非常简单地解决这个问题
  • 也许,当且仅当x 具有GetString() 方法时,您可以启用它,而不是禁用基本类型的功能。应该够了template &lt;typename T&gt; auto Function (T const &amp; x) -&gt; decltype( x.GetString(), void()) { SetString(x.GetString()); };

标签: c++ templates c++17 sfinae


【解决方案1】:

方法一

你可以使用std::enable_if,如下图:

C++11

//this function template will not be used with fundamental types
template<class T> typename std::enable_if<!std::is_fundamental<T>::value>::type Function(const T& x) 
{ 
    SetString(x.GetString()); 
    
};

Demo

C++17

template<class T> typename std::enable_if_t<!std::is_fundamental_v<T>> Function(const T& x) 
{ 
    SetString(x.GetString()); 
    
};

Demo

方法二

我们可以使用SFINAE。这里我们使用decltype逗号操作符来定义函数模板的返回类型。

//this function template will work if the class type has a const member function named GetString
template <typename T> auto Function (T const & x) -> decltype( x.GetString(), void()) 
{ 
    SetString(x.GetString());   
};

Demo

这里我们使用了尾随返回类型语法来指定函数模板的返回类型。

【讨论】:

    【解决方案2】:

    如果问题是 int 不支持 GetString() 方法,则可能不是禁用基本类型的功能,而是在(且仅当)模板类型具有 GetString() const 方法时启用它接受不带参数的调用。

    注意GetString() 必须是const,因为Function() 接收const 引用,所以只有当GetString()const 方法时,您才能在Function() 内调用GetString()

    以下是一个完整的编译示例。观察bar1bar2案例中的失败

    #include <string>
    
    void SetString (std::string const &) 
    { }
    
    struct foo // class with a conformat GetString method
    { std::string GetString () const { return "abc"; } };
    
    struct bar1 // class with a not conformant (not const) GetString method
    { std::string GetString () { return "123"; } };
    
    struct bar2 // class with a not conformant (require a int) GetString method
    { std::string GetString (int) const { return "123"; } };
    
    struct bar3 // class without a GetString method
    { };
    
    
    template <typename T>
    auto Function (T const & x) -> decltype( x.GetString(), void())
    { SetString(x.GetString()); }
    
    int main()
    {
      Function(foo{}); // compile
    
      // Function(bar1{}); // compilation error (GetString isn't const)
      // Function(bar2{}); // compilation error (GetString require a int)
      // Function(bar3{}); // compilation error (no GetString method)
      // Function(0);      // compilation error (no GetString method)
    }
    

    【讨论】:

    • 你能指出一些关于“-> decltype”做什么的资源吗?似乎是一个有趣的概念。
    • @mrzacekmrzacek 这只是一个trailing return type(见def 2)
    • @mrzacekmrzacek -&gt; 被称为(在这种情况下)trailing return type syntax,如我的回答中所述。在上述链接中,有一个示例template&lt;class T, class U&gt; auto add(T t, U u) -&gt; decltype(t + u);。也可以参考this
    • @mrzacekmrzacek - 查看 Mgets 建议的链接。无论如何,这个想法是编译器使用decltype()检测调用x.GetString()的类型; if 不是一个有效的表达式,该函数未启用;如果它是一个有效的表达式,则逗号运算符丢弃x.GetString() 的类型并返回(即:说Function() 返回)void() 的类型,即void。纯属SFINAE
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多