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

C++ C++;主、模板化、专门化和别名模板参数的模板专门化,c++,templates,c++17,template-meta-programming,template-specialization,C++,Templates,C++17,Template Meta Programming,Template Specialization,我正在尝试专门化一个模板,以便同时使用主类型、模板化类型和专门化/别名模板化类型。请参见下面的代码示例 它编译但不链接 如何为zero()编写一个模板专门化,我可以将其称为zero(),并且仍然能够调用zero等 原因是,在我的应用程序中,我没有得到myvec的大小N,因此我无法编写zero(),但是,我知道myvec是一个模板别名,如下所示,我知道模板的结构等,只是不知道大小: #include <iostream> #include <array> #include

我正在尝试专门化一个模板,以便同时使用主类型、模板化类型和专门化/别名模板化类型。请参见下面的代码示例

它编译但不链接

如何为
zero()
编写一个模板专门化,我可以将其称为
zero()
,并且仍然能够调用
zero

原因是,在我的应用程序中,我没有得到
myvec
的大小
N
,因此我无法编写
zero()
,但是,我知道
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;
}
#包括
#包括


非常感谢你的帮助!(P.S.解决方案到CPP17是好的)

< P>您在C++中使用部分专用化但部分专业化是不允许的,在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<double>();
zero<double>::func();
因为必须创建所需类型的对象,所以

// .........VV
zero<double>{}();
其中,
double
专门化是

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

{静态双FUNC(){STD::CUT

您不希望在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<double>();
zero<double>::func();
因为必须创建所需类型的对象,所以

// .........VV
zero<double>{}();
其中,
double
专门化是

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

{static double func(){std::cout在@max66 answer之后,您可以保留原始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...>{}();
}
//泛型声明
模板
结构零度;
//专业
模板
结构零度{
双运算符(){

std::cout以下@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...>{}();
}
//泛型声明
模板
结构零度;
//专业
模板
结构零度{
双运算符(){

再次感谢您提供的两个极好的答案。我非常喜欢可变模板解决方案;-)。 事实证明,对于所述的问题,有一个更简单的解决方案,尽管不太通用,但不需要用结构替换函数。只需在zero()的通用声明中添加一个定义,即change

template <typename T> T zero();
template T zero();

template T zero(){return{};};
代码编译和运行都很好。我觉得有一点解释我为什么要做这些愚蠢的事情是正确的:在我的应用程序中,我混合了不同大小的Eigen::Matrix、Eigen::Tensor等类型的对象(通过模板别名实现)主要标量类型。不幸的是,本征类型没有通过
{}
进行标准零初始化,而是具有静态setZero()函数。上述解决方案允许我使用本征特定初始化作为标准,然后完全专用于标量类型


我更新了

再次感谢您的两个优秀答案。我非常喜欢可变模板解决方案;-)。 事实证明,对于所述的问题,有一个更简单的解决方案,尽管不太通用,但不需要用结构替换函数。只需在zero()的通用声明中添加一个定义,即change

template <typename T> T zero();
template T zero();

template T zero(){return{};};
代码编译和运行都很好。我觉得有一点解释我为什么要做这些愚蠢的事情是正确的:在我的应用程序中,我混合了不同大小的Eigen::Matrix、Eigen::Tensor等类型的对象(通过模板别名实现)主要标量类型。不幸的是,本征类型没有通过
{}
进行标准零初始化,而是具有静态setZero()函数。上述解决方案允许我使用本征特定初始化作为标准,然后完全专用于标量类型


我更新了

谢谢你的回答。我之前也在考虑函数的部分专业化是禁止的问题。但是,函数的部分专业化应该会导致编译时错误,不是吗?我的代码是编译的,但没有链接。另外,请注意我是“专业化的”通过
使用别名
。据我所知,这对编译器来说是透明的。对我来说,从
z4
z
的转换已经是一个局部特化-std:array,double被vec_t取代。似乎应该可以进行额外的步骤,同时去掉N。@JReichardt-你写的东西en不是部分专门化:是重载。但是,这样一来,你就不能按你想要的方式调用函数(
zero
),你必须以
zero()
的方式调用。链接器错误来自于
zero()
匹配未定义的初始(通用)声明(所以编译)(所以链接错误)。似乎我对重载和专门化的确切区别感到困惑。你会说我重载了返回类型吗?Zero()不接受任何参数。总之,您是对的-定义泛型声明解决了问题-请参阅下面。再次感谢。专门化和重载之间的区别不容易在注释中解释…您的前三个专门化(
double
v\u t
v5\u t
)是第一个声明的专门化,接收单个模板参数(类型名)。以下
zero()
V
V
)基于不同的模板参数,因此与原始泛型声明不匹配