【问题标题】:Determine the correct size type for the result of a binary operation between two vector types or one vector type and a scalar type确定两种向量类型或一种向量类型与标量类型之间二元运算结果的正确大小类型
【发布时间】:2016-04-11 12:27:04
【问题描述】:

请考虑以下代码sn-p:

template<class E>
class vector_expression {};

template<class Tuple>
class vector
    : public vector_expression<vector<Tuple>>
{
public:
    using value_type = typename Tuple::value_type;
    using size_type = typename Tuple::size_type;
};

namespace detail
{
    template<class E>
    constexpr bool is_vector_expression_v = std::is_base_of_v<vector_expression<std::decay_t<E>>, std::decay_t<E>>;

    template<class E, class = void>
    struct value_type { using type = std::decay_t<E>; };
    template<class E>
    struct value_type<E, std::enable_if_t<is_vector_expression_v<E>>> { using type = typename std::decay_t<E>::value_type; };

    template<class E>
    using value_type_t = typename value_type<E>::type;

    template<class E1, class E2, class BinaryOperation>
    class vector_binary_operation
    {
    public:
        using value_type = std::result_of_t<BinaryOperation(value_type_t<E1>, value_type_t<E2>)>;
    };
}

E1E2 可能是派生自 vector_expression 的类型或用作 vectorvalue_type 的任何类型。

需要谨慎选择与vector_binary_operation 的结果相对应的vector 类型。它的value_type 等于被调用的BinaryOperation 的结果类型是完全有道理的。

但是,正确的size_type 更难推断。这是我想做的:

  • 如果E1E2vector_expressions,那么让size_type = std::conditional_t< std::numeric_limits<typename std::decay_t<E1>::size_type>::max() < std::numeric_limits<typename std::decay_t<E2>::size_type>::max(), typename std::decay_t<E1>::size_type, typename std::decay_t<E2>::size_type>>>;
  • 如果E1E2(比如E1)不是vector_expression,那么让size_type = typename std::decay_t&lt;E2&gt;::size_type

E1E2 都不是 vector_expressions 不会发生。但是,(将其作为次要问题)我应该将class = std::enable_if_t&lt;is_vector_expression_v&lt;E1&gt; || is_vector_expression_v&lt;E2&gt;&gt; 添加到vector_binary_operation 的模板参数列表中吗?

主要问题是:如何定义size_type 如上所述?

【问题讨论】:

    标签: c++ templates c++14 typetraits


    【解决方案1】:

    一种方法是使用额外的特征或丰富您当前的特征:

    您可以创建一个特征,将sizeof(E1::size_type) 用于vector_expression0 用于

    template<class E, class = void>
    struct value_type {
        using type = std::decay_t<E>;
        using size_type = char; // or better some dummy type
        static constexpr std::size_t size = 0; // To be the minimal size
    };
    template<class E>
    struct value_type<E, std::enable_if_t<is_vector_expression_v<E>>> {
        using type = typename std::decay_t<E>::value_type;
        using size_type = typename std::decay_t<E>::size_type;
        static constexpr std::size_t size = std::numeric_limits<size_type>::max();
    };
    

    然后

    template<class E1, class E2, class BinaryOperation>
    class vector_binary_operation
    {
    public:
        using value_type = std::result_of_t<BinaryOperation(value_type_t<E1>,
                                                            value_type_t<E2>)>;
        using size_type = std::conditional_t<(value_type<E1>::size < value_type<E2>::size),
                                             typename value_type <E1>::size_type,
                                             typename value_type <E2>::size_type>>>;
    };
    

    【讨论】:

    • value_type::size_type 听起来有些奇怪,因为 value_typesize_type 之间的关系松散。所以,我认为最好使用带有成员typemax 的新结构size_type。不过,非常感谢您的回答!
    • size 在专业化中的类型应该是size_typestd::size_t can store the maximum size of a theoretically possible object of any type,但此语句与 sizeof 运算符有关。 size_type 出现在 vector 中可能是一些抽象的复杂事物。虽然这种情况不寻常且不太可能,但代码在这种情况下也应该可以工作。
    • @0xbadf00d:重命名为vector_trait_helper 之类的名称后,将struct 合二为一可能有意义,但我同意我应该更改名称,或者创建另一个struct .
    • 对于大小,我首先使用sizeof,然后才看到您使用max()...但它为您提供了足够的信息来适应您的用例:-)
    【解决方案2】:

    通常我发现在这种情况下,通常最简单的方法是定义一组重载auto Foo(E1,E2) -&gt; ResultType 来进行实际操作,然后设置vector_binary_operation::size_type = decltype(Foo(E1,E2))

    在您的特定情况下,您将拥有三个重载:对于 E1、E2 或两者都是 vector_expression。

    在这种特殊情况下,我看到了另一种选择。介绍一个具有专业化 template&lt;typename T&gt; class sizer&lt;vector_expression&lt;T&gt;&gt; { const int max = std::numeric_limits&lt;typename std::decay_t&lt;E1&gt;::size_type&gt;::max() ; } 的助手 template&lt;typename T&gt; class sizer { const int max = 0; }

    这背后的想法很简单。您的一般表达式比较来自两个向量表达式的两个属性,并选择该属性最大的类型。通过将此属性定义为所有其他类型的零,所有其他类型的排名低于您的 vector_typs 并且不会被选中。

    【讨论】:

    • 关于您的第一种方法:size_typevector_binary_operation 中尤其需要作为索引operator[] 的正确类型。我看不出如何将这种类型推断为操作Foo 的结果类型。也许你有一个想法。
    • 请注意,在您的第二种方法中,我们需要使用Jarod42 提出的enable_if 解决方案,因为E1 (通常)是派生自vector_expression 的类型。
    • @0xbadf00d:我也不知道;这是您的问题域。这个想法的核心是你有3个不同的公式来计算一个类型,这取决于输入。通过使用函数重载,您可以在三个函数声明中使用这三个不同的公式,然后选择适当的一个。请注意,您甚至不需要调用函数,decltype(Foo(...)) 中的函数重载都是从 Foo 的声明中解决的。
    • 模板特化不需要enable_if。第二个模板比第一个更专业,所以当第二个可用时,第一个将不会被使用。
    • 当然,但不会为vector 选择专业化。请注意,您需要将E1 替换为T。此外,max 应该是静态的,您应该使用 struct 而不是 classmax 在您的声明中是 private)。最后但同样重要的是,特化中的max 应该是size_type 类型,因为你不知道numeric_limits::max() 的值是否可以存储在int 中。
    猜你喜欢
    • 2011-02-07
    • 1970-01-01
    • 2011-11-29
    • 2015-08-12
    • 1970-01-01
    • 2016-03-27
    • 2012-08-17
    • 2013-02-19
    • 1970-01-01
    相关资源
    最近更新 更多