主要、模板化、专门化和别名模板参数的 C++ 模板特化

C++ Template specializations for primary, templated, specialised and aliased template arguments

我正在尝试专门化一个模板以同时处理主要类型、模板化类型和 specialised/aliased 模板化类型。请参阅下面的代码示例。

编译但不编译link。

我如何为 zero() 编写模板特化,我可以将其调用为 zero<myvec>() 并且仍然能够调用 zero<double> 等?

原因是在我的申请中,没有给出myvec的大小N,所以我不能写zero<vec_t, N>(),但是,我知道myvec ] 是一个模板别名,如下所示,我知道模板的结构等,只是不知道大小:

#include <iostream>
#include <array>
#include <vector>

template<std::size_t N>
using vec_t = std::array<double, N>;

using v_t = std::vector<double>;

using v5_t = vec_t<5>;

// generic declaration
template<typename T> T zero();

// full specialization to double 
template<> double zero() { std::cout << " -> Double\n"; return 0;}
// full specialization to v_t
template<> v_t zero() { std::cout << " -> vector<double>\n"; return v_t{}; };
// full specialization to v5_t
template<> v5_t zero() { std::cout << " -> vec_t<5>\n"; return v5_t{}; };

// attempt at partial specialization to vec_t<N>
template<template<typename T, std::size_t N> typename V, typename T, std::size_t N> V<T, N> zero() {
std::cout << " -> V<T, N>\n";
return V<T, N>{}; 
};
template<template<std::size_t N> typename V, std::size_t N> V<N> zero() {
std::cout << " -> V<N>\n";
return V<N>{};
};


int main() {

  double z1 = zero<double>(); // works

  v_t z2 = zero<v_t>(); // works

  v5_t z3 = zero<v5_t>(); // works

  const std::size_t N = 6;
  vec_t<N> z4 = zero<std::array, double, N >(); // works, but requires full specs of vec_t

  vec_t<N> z5 = zero<vec_t, N>(); // works, but requires N

  using myvec = vec_t<6>;
  myvec z6 = zero<myvec>(); // linker error ! but is what I'd like to write

  return 0;
}

你可以看看CompilerExplorer

非常感谢您的帮助! (P.S。高达 Cpp17 的解决方案都很好)

您正在寻找部分特化,但在 C++ 中,对于函数,部分特化是不允许的。

但是对于 类 是允许的,所以,如果可以接受将 zero() 模板函数替换为模板 类 中的方法,您可以编写如下内容

#include <iostream>
#include <array>
#include <vector>

template<std::size_t N>
using vec_t = std::array<double, N>;

using v_t = std::vector<double>;

using v5_t = vec_t<5>;

// generic declaration
template <typename T>
struct zero;

// specializations

template <>
struct zero<double>
 { double operator() () { std::cout << " -> double\n"; return {};} };

template <>
struct zero<v_t>
 { v_t operator() () { std::cout << " -> v_t\n"; return {};} };

template <>
struct zero<v5_t>
 { v5_t operator() () { std::cout << " -> v5_t\n"; return {};} };

template <std::size_t N>
struct zero<vec_t<N>>
 { vec_t<N> operator() () { std::cout << " -> vec_t + N\n"; return {};} };

template <template <std::size_t> class C, std::size_t N>
struct zero<C<N>>
 { C<N> operator() () { std::cout << " -> C + N\n"; return {};} };


int main() {

  double z1 = zero<double>{}(); 

  v_t z2 = zero<v_t>{}();

  v5_t z3 = zero<v5_t>{}();

  constexpr std::size_t N = 6;

  vec_t<N> z4 = zero<std::array<double, N>>{}(); 

  vec_t<N> z5 = zero<vec_t<N>>{}();

  using myvec = vec_t<6>;

  myvec z6 = zero<myvec>{}(); // now works
}

观察到不能再调用zero()如下

zero<double>();

因为你必须创建一个所需类型的对象,所以

// .........VV
zero<double>{}();

如果这是一个问题,您可以使用静态方法,所以如果您可以为该方法命名(比如 func()),您可以按如下方式调用它

zero<double>::func();

double 专业

template <>
struct zero<double>
 { static double func () { std::cout << " -> double\n"; return {};} };

按照@max66 的回答,您可以保留原来的 API 但将调用委托给模板结构:

// generic declaration
template <typename T>
struct zero_s;

// specializations
template <>
struct zero_s<double> {
    double operator() () {
        std::cout << " -> double\n"; return {};
    }
};

template <template <typename T, std::size_t> class C, typename T, std::size_t N>
struct zero_s<C<T, N>> {
    C<Type, N> operator() () {
        std::cout << " -> C<T, Size>, Size = " << N << "\n";
        return {};
    }
};

template <std::size_t N> struct zero_s<vec_t<N>> {
    vec_t<N> operator() () {
        std::cout << " -> vec_t + N: " << N << "\n"; return {};
    }
};
    
// ...    
 
//------------------------------------------------
// generic "zero" - still a free function
//------------------------------------------------
template<typename T, typename... Ts> T zero() {
    return zero_s<T, Ts...>{}();
}

参见:https://godbolt.org/z/cEcvqK

再次感谢两位出色的回答。我真的很喜欢可变参数模板解决方案 ;-)。 事实证明,有一个更简单的解决方案,虽然公认的不太通用,但无需用结构替换函数即可解决上述问题。只需在 zero() 的通用声明中添加一个定义,即 change

template <typename T> T zero();

template <typename T> T zero() {return {};};

并且代码可以正常编译和运行。对于我为什么做这些愚蠢的事情,我觉得有点解释是有道理的:在我的应用程序中,我将 Eigen::Matrix、Eigen::Tensor 等类型的对象与不同大小(通过模板别名实现)混合在一起主要标量类型。不幸的是,Eigen 类型没有通过 {} 进行标准的零初始化,而是具有静态 setZero() 函数。上面的解决方案允许我使用 Eigen 特定的初始化作为标准,然后完全专注于标量类型。

我更新了 CompilerExplorer