【问题标题】:How to specialize template for containers and enums如何专门化容器和枚举的模板
【发布时间】:2019-11-20 00:18:07
【问题描述】:

我正在尝试为枚举类型和 stl 容器类型专门提供一个简单的功能。 SFINAE 的想法适用于使用 enable_if 的枚举,但是类似的 stl 容器技术不起作用(我知道依赖 value_type 的存在并假设它是容器不是一个好主意,但这不是重点)。

template <typename T, typename = void>
    struct wrapper {
        static T getValue()
        {
            std::cout<<"\n WRAPPER DEFAULT VERSION IS CALLED.\n";
            return T();
        }
    };

    template<typename T>
    struct wrapper<T,typename std::enable_if<std::is_enum<T>::value>::type>{
        static T getValue()
        {
            std::cout<<"\n WRAPPER ENUM VERSION IS CALLED.\n";
            return T();
        }
    };

    template<typename Container>
    struct wrapper<Container, typename Container::value_type> {
        static Container getValue()
        {
            std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
            return Container();
        }
    };


int main()
{
    //En is an enum type
    En en = (En) wrapper<En>::getValue(); //Prints ENUM VERSION

    std::vector<int> vec;
    vec = wrapper<std::vector<int>>::getValue(); //Prints DEFAULT VERSION
}

请告诉我为什么第二次调用会转到默认实现?

解决方案:

感谢 Sam Varshavchik,我发现我错过了第二个参数应该解析为 void 的点(就像在 enable_if::type 的情况下一样),否则我必须显式传递第二个参数才能进行调用解析为容器版本: wrapper&lt;std::vector&lt;int&gt;, int&gt;::getValue();

为了使原始版本能够正常工作(在 C++ 17 之前 void_t 不可用),我正在创建自己的类型特征,这依赖于容器具有为其定义的迭代器类型这一事实:

    template <typename T1, typename T2 = void>
    struct container_trait
    {};

    template <typename T1>
    struct container_trait<T1, typename T1::iterator> {
        typedef void type;
    };

现在我的容器版本的包装器变成了:

    template<typename Container>
    struct wrapper<Container, typename container_trait<Container, typename Container::iterator>::type> {
        static Container getValue(const rapidjson::Value& rjv)
        {
            std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
            return Container();
        }
    };

现在同样的调用工作得很好:

vec = wrapper&lt;std::vector&lt;int&gt;&gt;::getValue(); //Prints CONTAINER VERSION

【问题讨论】:

  • 您的容器专业化需要两个模板参数(第二个不是 void)。因此,在您的示例中永远不会调用它。您可能需要编写类似于 is_enum 的类型特征,但对于包含 value_type 的标准容器(根据您的要求),然后以相同的方式使用它。查看std::void_t 以及它是如何使用的。
  • 感谢@DeiDei,我最终为容器编写了一个类型特征,它依赖于 value_type 的存在(尽管这种对 value_type 的依赖听起来对我来说不是很有说服力)。但目前想不出更好的方法。
  • 也许最好检查const_iterator,因为它与容器更相关。还要检查stackoverflow.com/questions/12042824/…

标签: c++ templates sfinae


【解决方案1】:

第二次调用默认实现的原因很简单。

只需手工计算出容器版本模板参数的参数是如何推导出来的:

template<typename Container>
struct wrapper<Container, typename Container::value_type>

您正在实例化以下模板:

wrapper<std::vector<int>>

所以:

1) Containerstd::vector&lt;int&gt;

2) Container::value_typeint

因此这个专业化变成:

struct wrapper<std::vector<int>, int>

但是你只是在调用:

wrapper<std::vector<int>, void>

因为void 是第二个模板参数的默认值,所以这匹配了错误的特化。

解决方案很简单,容器特化应该是:

#include <type_traits>

template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {

std::void_t 是 C++17,stackoverflow.com 上还有其他问题解释了如何为早期的 C++ 标准实现它。完整示例:

#include <vector>
#include <iostream>
#include <type_traits>

enum En {};

template <typename T, typename = void>
struct wrapper {
        static T getValue()
        {
        std::cout<<"\n WRAPPER DEFAULT VERSION IS CALLED.\n";
        return T();
        }
};

template<typename T>
struct wrapper<T,typename std::enable_if<std::is_enum<T>::value>::type>{
        static T getValue()
        {
        std::cout<<"\n WRAPPER ENUM VERSION IS CALLED.\n";
        return T();
        }
};

template<typename Container>
struct wrapper<Container, std::void_t<typename Container::value_type>> {
        static Container getValue()
        {
        std::cout<<"\n WRAPPER CONTAINER VERSION IS CALLED.\n";
        return Container();
        }
};


int main()
{
    //En is an enum type
    En en = (En) wrapper<En>::getValue(); //Prints ENUM VERSION

    std::vector<int> vec;
    vec = wrapper<std::vector<int>>::getValue(); //Prints DEFAULT VERSION
}

结果:

调用了封装枚举版本。

调用了包装容器版本。

【讨论】:

  • 感谢 Sam,您的回答帮助我达到了我在这里遗漏的点,即第二种类型应该被解决为无效(如 enable_if 的情况)。而且由于我没有使用 C++ 17,所以我现在使用的是类型特征而不是 void_t。顺便说一句,有没有专门针对 stl 容器类型的标准方法?因为我觉得依赖 value_type 或 iterator 等的存在对我来说并不是很有说服力。
  • 几乎所有容器都定义了value_type。这与任何其他方法一样好。
猜你喜欢
  • 1970-01-01
  • 2012-09-25
  • 1970-01-01
  • 1970-01-01
  • 2022-01-15
  • 2011-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多