【问题标题】:How do I use std::enable_if with a self-deducing return type?如何将 std::enable_if 与自推断返回类型一起使用?
【发布时间】:2014-01-09 16:27:57
【问题描述】:

C++14 将具有可以根据返回值推断返回类型的函数。

auto function(){
    return "hello world";
}

我可以将此行为应用于使用 enable_if 的函数返回类型成语吗?

例如,让我们考虑以下两个函数:

#include <type_traits>
#include <iostream>

//This function is chosen when an integral type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_integral<T>::value>::type {
    std::cout << "integral" << std::endl;
    return;
}

//This function is chosen when a floating point type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_floating_point<T>::value>::type{
    std::cout << "floating" << std::endl;
    return;
}

int main(){

  function(1);    //prints "integral"
  function(3.14); //prints "floating"

}

如您所见,使用 SFINAE 通过返回类型习语选择正确的函数。 但是,这些都是 void 函数。 enable_if 的第二个参数默认设置为void。这将是相同的:

//This function is chosen when an integral type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_integral<T>::value, void>::type {
    std::cout << "integral" << std::endl;
    return;
}

//This function is chosen when a floating point type is passed in
template<class T >
auto function(T t) -> typename std::enable_if<std::is_floating_point<T>::value, void>::type{
    std::cout << "floating" << std::endl;
    return;
}

我可以对这两个函数做些什么,让它们的返回类型由返回值推导出来?

gcc 4.8.2(使用--std=c++1y

【问题讨论】:

    标签: c++ template-meta-programming typetraits c++14 enable-if


    【解决方案1】:

    std::enable_if 不必在返回类型中,从 C++11 开始,它可以是模板参数的一部分。

    所以你的等效函数可以是(或者,嗯,有这种效果的东西):

    enum class enabler_t {};
    
    template<typename T>
    using EnableIf = typename std::enable_if<T::value, enabler_t>::type;
    
    //This function is chosen when an integral type is passed in
    template<class T, EnableIf<std::is_integral<T>>...>
    auto function(T t) {
        std::cout << "integral" << std::endl;
        return;
    }
    
    //This function is chosen when a floating point type is passed in
    template<class T, EnableIf<std::is_floating_point<T>>...>
    auto function(T t) {
        std::cout << "floating" << std::endl;
        return;
    }
    

    也可以是函数中的参数:

    //This function is chosen when an integral type is passed in
    template<class T>
    auto function(T t, EnableIf<std::is_integral<T>>* = nullptr) {
        std::cout << "integral" << std::endl;
        return;
    }
    
    //This function is chosen when a floating point type is passed in
    template<class T>
    auto function(T t, EnableIf<std::is_floating_point<T>>* = nullptr) {
        std::cout << "floating" << std::endl;
        return;
    }
    

    这将保留自动类型扣除和 SFINAE。

    【讨论】:

    • 我不明白第一个示例中的“...”。这是否以某种方式为 enabler_t 赋予了价值?
    • @TrevorHickey,不,它声明了一个非类型模板参数包,它接受零个或多个enabler_t 类型的参数。实际上,当您调用function(x) 时,参数包将被推断为空(在这种情况下,调用将不明确,因此第一个示例不起作用)。
    • @JonathanWakely 在 GCC 中按我期望的方式工作,但在 Clang 中却没有,因为据我所知,Clang 不接受非类型模板参数包。虽然在实践中我通常设置DisableIfs 或否定其他EnableIfs 的先前条件。
    • 如果一个函数有多个参数,上面使用带有EnableIf 的可变参数模板会妨碍。在这种情况下,请尝试使用 EnableIf&lt;std::is_integral&lt;T&gt;&gt; = enabler_t{}
    • 我还是不喜欢这些解决方案。是的,我确实在乎那个错误调用function&lt;float,enabler_t{}&gt;(42)的程序员。
    【解决方案2】:

    std::enable_if 可以是返回类型、函数参数或模板参数。如果使用返回类型或模板参数会出现函数重定义错误,因此需要使用std::enable_if作为函数参数:

    #include <type_traits>
    #include <iostream>
    
    template<class T, typename = typename std::enable_if<std::is_integral<T>::value, void>::type>
    auto function(T t, typename std::enable_if<std::is_integral<T>::value, void>::type* dummy = nullptr) {
        std::cout << "integral" << std::endl;
        return 0;
    }
    
    //This function is chosen when a floating point type is passed in
    template<class T, typename = typename std::enable_if<std::is_floating_point<T>::value, void>::type>
    auto function(T t, typename std::enable_if<std::is_floating_point<T>::value, void>::type* dummy = nullptr) {
        std::cout << "floating" << std::endl;
        return 0.0f;
    }
    
    int main() 
    {
        auto ret = function(0); // integral
        auto ret2 = function(0.0f); // floating
        std::cout << std::boolalpha << std::is_integral<decltype(ret)>::value << std::endl; // true
        std::cout << std::is_floating_point<decltype(ret2)>::value << std::endl; // true
    }
    

    【讨论】:

    • +1。这个解决方案比@Rapptz 提出的要好:在这个解决方案中不需要额外的类,它可以在 gcc 和 clang 中编译。
    • 在此解决方案的基础上,添加了一个更简单的版本,其中 enable_if 仅作为模板参数出现,而不作为方法参数出现。
    【解决方案3】:

    在@user1508519 的答案行中,我们可以从方法中删除 enable_if 参数,并将其仅保留为模板参数。 我们依赖于enable_if&lt;false&gt; 没有定义type 的事实,因此不存在的enable_if&lt;false&gt;::type 是SFINAE 的一个好工具——在包含模板参数的方法签名中。

    不需要在方法本身中实际使用这个模板参数!

    因此:

    template<class T,
    typename std::enable_if<std::is_integral<T>::value>::type* dummy = nullptr>
    auto function(T t) {
        std::cout << "integral" << std::endl;
        return 0;
    }
    
    // This function is chosen when a floating point type is passed in
    template<class T, 
    typename std::enable_if<std::is_floating_point<T>::value>::type* dummy = nullptr>
    auto function(T t) {
        std::cout << "floating" << std::endl;
        return 0.0f;
    }
    
    int main() {
        auto ret = function(0); // integral
        auto ret2 = function(0.0f); // floating
        cout << std::boolalpha;
        cout << std::is_integral<decltype(ret)>::value << endl; // true
        cout << std::is_floating_point<decltype(ret2)>::value << endl; // true
    }
    

    输出

    integral
    floating
    true
    true
    

    【讨论】:

    【解决方案4】:

    正如在别处提到的std::enable_if 可用于形成任一返回类型;函数参数;或模板参数。

    但是,后两种方法的缺点是它们会改变相关函数或对象的签名。另一方面,在返回类型中使用 std::enable_if 会使函数和模板参数计数保持不变。

    随着 C++11 中 lambda 返回类型的自动推断(可能扩展到 C++14 中的普通函数),如果有一种技术可以同时推断返回类型,那将是理想的; 并且在返回类型上使用std::enable_if。让你的蛋糕吃掉 - 几乎。唉,目前看来这是不可能的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-30
      • 2017-06-04
      • 1970-01-01
      • 2019-02-12
      • 2015-02-09
      相关资源
      最近更新 更多