当前位置: 首页 > 知识库问答 >
问题:

如何为 const 和非 const 类型的混合编写专门的可变参数模板

朱令
2023-03-14

当类型是一堆st向量时,我需要专门化一个可变参数模板d::

#include <iostream>

#include <vector>
#include <type_traits>

template<typename... Ts>
struct is_vector{
    is_vector(Ts&...){};
    static void print() {std::cout << "not vectors" <<std::endl;}
};

template<typename... Ts>
struct is_vector<std::vector<Ts>...>
{   is_vector(std::vector<Ts>&...){}
    static void print() {std::cout << "vectors" << std::endl;}
};




int main()
{
    
    auto const a3 = std::vector<double> {10., 20., 30.};
    auto a4 = std::vector<int> { 1,2,3};
    
    auto z = is_vector(a4,a3); z.print(); // print "not vectors"
    auto x = is_vector(a4,a4); x.print(); // print "vectors"
    
    return 0;
}

问题在于,当is_vector的参数包含常量和非常量向量的混合时,它使用默认类而不是专用类。当然,如果所有向量都是常量,我可以编写一个专门化,但如何处理混合是个问题。我试过这个

template<typename... Ts>
struct is_vector<typename std::remove_const<std::vector<Ts> >::type...>
{   is_vector(std::vector<Ts>&...){}
    static void print() {std::cout << "vectors" << std::endl;}
};

但是编译器 (Apple clang 版本 11.0.3 (clang-1103.0.32.62) 目标: x86_64-apple-darwin21.4.0) 抛出此错误

test.cpp:13:8:错误:类模板局部特化包含一个无法推导的模板参数;这个局部特化将永远不会被使用[-Wunusule-part al-专用化]结构is_vector


共有2个答案

尚楚
2023-03-14
匿名用户

分开关注。让< code>is_vector为一个输入、< code>const或其他输入工作。以此为基础构建变量版本。

例如

template<class T>
struct is_vector_struct
    : std::false_type {};

template<class T, class allocator>
struct is_vector_struct<std::vector<T, allocator>>
    : std::true_type {};

template<class T>
constexpr bool is_vector = is_vector_struct<std::remove_cvref_t<T>>::value;

static_assert(is_vector<std::vector<int> const&>);

对多个参数使用 fold 表达式:

template<class...Ts>
constexpr bool all_are_vectors = (is_vector<Ts> && ...);

如果您不能使用C 17,您可以使用递归来代替。

编辑:我给了你一个类型谓词,但你也想要分派。以下是我知道的基于谓词进行调度的技术。

你可以用

  • 如果配置exr
  • 需要(C 20),
  • SFINAE/enable_if(v3模拟需要的范围),
  • 标签调度(标准算法迭代器所做的)。

如果你想在基于类型的函数中做出决定,就像在你的例子中一样,我认为如果constexpr是阻力最小的路径:

template<class...Ts>
void message(Ts...xs) {
  if constexpr(all_are_vectors<Ts...>)
    std::cout << "all vectors";
  else 
    std::cout << "not all vectors";
}

如果您想有选择地从重载集中添加或删除某些内容,If constepr对您没有帮助。这是所要求的启用_。

template<class...Ts>
void message(Ts...) { std::cout << "default message"; }

template<class T, std::size_t N>
void message(std::array<T,N>) { std::cout << "one array"; } 

template<class...Ts>
requires all_are_vectors<Ts...>
void message(Ts...) { std::cout << "all vectors"; }

因为< code>requires是一个语言级功能,所以编译器将尝试优先处理最具体的< code>requires重载。使用< code>enable_if和variadics,您需要帮助编译器解决一些问题:

template<class...Ts,
         class = std::enable_if_t<!all_are_vectors<Ts...>>>
void message(Ts...) { std::cout << "default message"; }

template<class...Ts,
         class = std::enable_if_t<all_are_vectors<Ts...>>>
void message(Ts...) { std::cout << "all vectors"; }

然后是标签分派。如果您有办法获得描述您的输入的虚拟标签类型,您可以使用常规函数重载来分派。这里比enable_if的优势是,您可以从更通用的标签类型继承更具体的标签类型。例如,std::random_access_iterator_tag继承自std::forward_iterator_tag。这是它在这种情况下的样子:

struct default_tag {};

struct all_vectors_tag{};

template<class...Ts>
void message_dispatch(default_tag, Ts...) { 
  std::cout << "not all vectors"; 
} 

template<class...Ts>
void message_dispatch(all_vectors_tag, Ts...) {
  std::cout << "all vectors"; 
} 

template<class...Ts>
using get_tag = 
   std::conditional_t<all_are_vectors<Ts...>, 
                      all_are_vectors_tag, 
                      default_tag>;

template<class...Ts>
void message(Ts...xs) { 
  message_dispatch(get_tag<Ts...>{}, xs...); 
}

茹高义
2023-03-14

有了C 20概念,你可以这样做:

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

template<typename T>
concept Vector = std::is_same_v<
    std::remove_cvref_t<T>,
    std::vector<typename T::value_type, typename T::allocator_type>
>;

template<typename... Ts>
struct is_vector {
    is_vector(Ts&...) {}
    static void print() {std::cout << "not vectors" <<std::endl;}
};

template<Vector... Ts>
struct is_vector<Ts...> {   
    is_vector(Ts&...) {}
    static void print() {std::cout << "vectors" << std::endl;}
};

int main()
{
    auto const a3 = std::vector<double> {10., 20., 30.};
    auto a4 = std::vector<int> { 1,2,3};
    
    static_assert(Vector<decltype(a3)>);
    static_assert(Vector<decltype(a4)>);
    static_assert(!Vector<int>);

    auto z = is_vector(a4,a3); z.print(); // prints "vectors"
    auto x = is_vector(a4,a4); x.print(); // prints "vectors"
    
    return 0;
}

请注意,根据具体情况,您还可以借此机会使用与代码使用的 std::vector api 的子集匹配的更广泛概念来打开任何像 std::vector 一样运行的容器。这肯定是更惯用的方法。

 类似资料:
  • 也许我累了,但是我被这个简单的局部特化卡住了,它不起作用,因为指定模板参数: 将替换为,或没有帮助。那么这种专门化是否可能呢?

  • 你也许注意到了,函数after和addTime的参数都是传递引用。这俩函数是纯函数,不修改接受的参数值,因此我也可以传值。 传值的好处是调用函数和被调用函数都进行了适当的封装--其中一方的修改不可能影响另一方,除非影响了返回值。 另一方面,传引用由于避免了参数的复制,往往更高效。除此之外,C++有一个优秀的特性叫做const,它能使引用参数和值参数一样安全。 If you are writing

  • 我试图从一组可变的模板类(其中每个类都有一个非类型参数)中恢复非typename模板参数,以便在另一种类型中将它们用作整数序列。 下面的代码显示了我所拥有的。整数序列/成员序列是从元组中抄袭出来的。 和的类型是

  • 我有一个编译器错误问题,请查看以下代码: 问题在于编译器忽略了 foo 函数上的“const”,使得对 foo 的调用非法(const int* to int*)。 严重程度代码描述项目文件行抑制状态错误C2664'ululfoo(const::类型)':无法将参数1从'const int*'转换为'const Mystrt::类型' 我在Visual Studio和gcc的5.3编译器中测试了以

  • 受这个答案的启发,我生成了这段代码,其输出取决于编译器: 如果使用 GCC 11 编译,调用