【问题标题】:Is it possible to use std::enable_if to select a member template specialization?是否可以使用 std::enable_if 来选择成员模板专业化?
【发布时间】:2012-10-26 10:02:01
【问题描述】:

给定一个类声明

class A {
    template <typename T> T foo();
};

我想将A::foo 专门用于T 的各种类型(int,...)和类型类(POD,非 POD)。不幸的是,我似乎不能为后者使用std::enable_if。以下内容无法编译:

template <> int A::foo<int>(); // OK

template <typename T> 
typename std::enable_if<is_pod<T>::value, T>::type foo(); // <<<< NOT OK!

template <typename T> 
typename std::enable_if<!is_pod<T>::value, T>::type foo(); // <<<< NOT OK!

问题可能是由于 std::enable_if&lt;...&gt; 是函数签名的一部分,并且我没有在 A 中声明任何此类成员。那么如何根据类型特征对模板成员进行专门化呢?

【问题讨论】:

    标签: c++ templates c++11 template-specialization


    【解决方案1】:

    我认为没有理由在这里专门化,重载函数似乎就足够了。

    struct A
    {
        template <typename T>
        typename std::enable_if<std::is_integral<T>::value, T>::type foo()
        {
            std::cout << "integral" << std::endl;
            return T();
        }
    
        template <typename T>
        typename std::enable_if<!std::is_integral<T>::value, T>::type foo()
        {
            std::cout << "not integral" << std::endl;
            return T();
        }
    }
    

    在检查 POD 或无 POD 时,您只有这两个选择,因此不需要更通用的函数(也不允许,因为它会模棱两可)。你需要更多吗?您可以在 std::enable_if&lt;std::is_same&lt;int, T&gt;::value, T&gt;::type 的帮助下检查显式类型而无需专门化。

    【讨论】:

    • @Daniel Gehriger enable_if 用于有条件地从重载解决方案中删除函数,因此非常适合这种情况。
    • 是的,但我仍然有 A::foo&lt;int&gt;()(以及某些特定类型的许多其他专业化),这将与 enable_if 选择的任何内容发生冲突。
    • 我的想法是用template &lt;typename T&gt; typename std::enable_if&lt;std::is_same&lt;int, T&gt;::value, T&gt;::type foo() { /*...*/ }替换那些应该和你的专业有相同的效果并解决冲突
    • 对 - 但我们必须将所有否定断言添加到其他也匹配类型 (std::is_integral&lt;T&gt;::value &amp;&amp; !std::is_same&lt;T, int&gt;::value) 的断言中,对吗?
    • 是的,你是对的。我想如果你有一个复杂的决策树分支,这可能会变得非常糟糕。
    【解决方案2】:

    我只是把它转发给一个结构,它可以很好地处理这个问题:

    #include <type_traits>
    #include <iostream>
    
    template <typename T, typename = void>
    struct FooCaller;
    
    class A {
    public:
        template <typename T>
        T foo() {
            // Forward the call to a structure, let the structure choose 
            //  the specialization.
            return FooCaller<T>::call(*this);
        }
    };
    
    // Specialize for PODs.
    template <typename T>
    struct FooCaller<T, typename std::enable_if<std::is_pod<T>::value>::type> {
        static T call(A& self) {
            std::cout << "pod." << std::endl;
            return T();
        }
    };
    
    // Specialize for non-PODs.    
    template <typename T>
    struct FooCaller<T, typename std::enable_if<!std::is_pod<T>::value>::type> {
        static T call(A& self) {
            std::cout << "non-pod." << std::endl;
            return T();
        }
    };
    
    // Specialize for 'int'.
    template <>
    struct FooCaller<int> {
        static int call(A& self) {
            std::cout << "int." << std::endl;
            return 0;
        }
    };
    

    【讨论】:

    • 这里使用std::enable_if 有点迂回。如果主模板承认typename = std::true_type 作为默认参数,那么例如typename std::is_pod&lt;T&gt;::type 是实现相同部分专业化的更短的方法。
    • 不错 - 但我需要从 FooCaller 访问 A 成员数据,所以我必须再次将调用转发给 A 的成员函数。这开始看起来非常复杂。
    • 好吧,只需简单的template&lt;class C&gt; using not_ = std::integral_constant&lt;bool, !C::value&gt;;。或者将其设置为 struct 用于尚不支持使用别名的编译器:template&lt;class C&gt; struct not_ : std::integral_constant&lt;bool, !C::value&gt;{}; 并在部分规范中使用它:FooCaller&lt;not_&lt;std::is_pod&lt;T&gt;&gt;::type&gt;
    • 最坏的情况你可以使用std::integral_constant&lt;bool, !std::is_pod&lt;T&gt;::value&gt;
    猜你喜欢
    • 2021-10-18
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-01
    • 2011-05-26
    • 2023-03-17
    相关资源
    最近更新 更多