【问题标题】:Accepting N arguments converting into the same type接受 N 个参数转换为相同类型
【发布时间】:2021-04-23 12:45:31
【问题描述】:

我目前正在尝试用 C++ 创建一个 vector 类。 该类应该有一个构造函数,它接受与向量中的维度一样多的参数。在实例化向量时,维度由模板定义。 所有扩充都应该能够转换为向量起源的相同数据类型(在模板中定义)。

这是我到目前为止写的(我在这里使用了很多其他问题的sn-ps):

// this section is to create an typedef struct all_same which checks if all elements are of the same type

template<typename ...T>  // head template
struct all_same : std::false_type { };

template<>  // accept if no elements are present
struct all_same<> : std::true_type { };

template<typename T>  // accept if only one element is present
struct all_same<T> : std::true_type { };

template<typename T, typename ...Ts>  // check if first and second value are the same type and recurse
struct all_same<T, T, Ts...> : all_same<T, Ts...> { };



template<typename T, size_t N>
class vec_abs {
public:
    vec_abs(const vec_abs &vec);  // construct from another vector
    explicit vec_abs(const std::array<T, N> &arr);  // construct from array

    template<typename ...Ts> requires // only compile if:
            all_same<typename std::decay<Ts>::type...>::type  // all are the same type (this might be optional, no sure)
            && std::conjunction<std::is_convertible<Ts, T>...>::value &&  // all can be converted to T
            (sizeof(Ts) == N)  // there are N arguments
    explicit vec_abs(const Ts&&... values);  // construct from multiple arguments

private:
    std::array<T, N> data;
};

代码的第一部分测试所有参数是否属于同一类型,取自question

我对这个解决方案不太确定(主要是因为它不起作用 :D),如果有任何改进建议,我将不胜感激。

感谢您的帮助!

【问题讨论】:

  • 您希望它们都属于同一类型(可以转换为T)还是都可以转换为T
  • @largest_prime_is_463035818 我希望它们可以转换为T,我只是无法使其正常工作(哇,响应速度也很快!)
  • 当你说“它不起作用”时,你能不能说得更具体一些,比如例子和期望等等。
  • 您在(sizeof...(Ts) == N) 中有错字,否则您发布的模板编译为:godbolt.org/z/Ys11ffqWq。也许在定义和实例化它时还有更多问题,但你没有表现出来。请包括minimal reproducible example
  • 你的代码有(sizeof(Ts) == N),但你想要sizeof...操作符,那就是(sizeof...(Ts) == N)

标签: c++ templates types variadic-templates variadic


【解决方案1】:

如果您不希望所有参数都属于同一类型,则可以删除该部分。除此之外,您还有一个错字:

(sizeof(Ts) == N) 

应该是

(sizeof...(Ts) == N) 

修复该问题后,转发参数并添加构造函数的定义,您的代码似乎可以执行您想要的操作:

#include <type_traits>
#include <array>
#include <iostream>

template<typename T, size_t N>
struct vec_abs {
    template<typename ...Ts> requires // only compile if:
            std::conjunction<std::is_convertible<Ts, T>...>::value &&  // all can be converted to T
            (sizeof...(Ts) == N)  // there are N arguments
    explicit vec_abs(Ts&&... values) : data({std::forward<Ts>(values)...}) {}
    std::array<T, N> data;
};

int main() {
    auto foo = vec_abs<int,3>(1,2,3);
    foo = vec_abs<int,3>(1.0,2,3);    
    for (const auto& e : foo.data) std::cout << e;

    auto bar = vec_abs<std::string,3>("foo",std::string{"bar"},"moo");
    for (const auto& e : bar.data) std::cout << e;
    // bar = vec_abs<std::string,3>(123,1.2); // error wrong number
    //bar = vec_abs<std::string,3>(123,1.2,42); // error cannot be converted
}

Output::

123foobarmoo

如果您确实想要所有参数都属于同一类型的约束...

正如其他答案所提到的,可以避免 all_same 中的递归。以下内容已经在 C++11 中工作。不幸的是,我再也找不到原始来源了。

template <typename T,typename...Ts>
struct all_same {
    static const bool value = std::is_same< all_same, all_same<Ts...,T>>::value;
};

【讨论】:

    【解决方案2】:

    递归模板非常昂贵(在编译时间和编译器内存使用方面),因此虽然它们可以工作,并且在历史上是解决某些解决方案的唯一方法,但现在可以使用更简洁和编译的更现代的技术快得多

    您可以使用折叠表达式将整个 all_same 替换为简单的 constexpr 函数:

    template <typename T, typename... Ts>
    constexpr bool all_same() {
        return (std::is_same_v<T, Ts> && ...);
    }
    
    static_assert( all_same<int, int, int>() );
    

    但是,鉴于它们都必须可转换为 T,为什么还要确保它们是完全相同的类型?这似乎是不必要的限制。您可以使用std::is_convertible_v&lt;From, To&gt; 轻松地将上面的函数更改为“all_convertible_to”。

    【讨论】:

    • 首先,感谢您的帮助和快速响应!我更喜欢使用std::is_convertible_v&lt;From, To&gt;,元素不必是同一类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    • 2012-07-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多