【问题标题】:Function not visible durring instantiation of variadic templates在可变参数模板实例化期间函数不可见
【发布时间】:2020-06-22 19:03:09
【问题描述】:

我正在尝试创建一个返回零填充数组的函数... 按照Elegantly define multi-dimensional array in modern C++我定义:

template<typename U, std::size_t N, std::size_t... M>                                                                                                                                                               
struct myTensor{                                                                                                                                                                                                    
  using type = std::array<typename myTensor<U, M...>::type, N>;                                                                                                                                                     
};                                                                                                                                                                                                                  
template<typename U, std::size_t N>                                                                                                                                                                                 
struct myTensor<U,N>{                                                                                                                                                                                               
  using type = std::array<U, N>;                                                                                                                                                                                    
};                                                                                                                                                                                                                  
template<typename U, std::size_t... N>                                                                                                                                                                              
using myTensor_t = typename myTensor<U, N...>::type;

然后我定义以下模板函数来填充零:

template<typename U, std::size_t N, std::size_t... M>                                                                                                                                                               
myTensor_t<U, N, M...> Zero_Tensor(){                                                                                                                                                                               
  myTensor_t<U, N, M...> res;                                                                                                                                                                                       
  for(int i=0; i<N; i++)                                                                                                                                                                                            
    res[i] = Zero_Tensor<U, M...>();                                                                                                                                                                                
  return res;                                                                                                                                                                                                       
};                                                                                                                                                                                                                  
                                                                                                                                                                                                                    
template<typename U, std::size_t N>                                                                                                                                                                                 
myTensor_t<U, N> Zero_Tensor(){                                                                                                                                                                                     
  myTensor_t<U, N> res;                                                                                                                                                                                             
  for(int i=0; i<N; i++)                                                                                                                                                                                            
    res[i] = U(0);                                                                                                                                                                                                  
  return res;                                                                                                                                                                                                       
};

例如当我这样做时

class myclass{
myTensor_t<int,3,3,5> x;
};

它编译得很好。如果我尝试这样做:

class myclass{
myTensor_t<int,3,3,5> x=Zero_Tensor<int,3,3,5>();
};

编译时出现以下错误:

src/mytensor.hpp(107): error: no instance of overloaded function "Zero_Tensor" matches the argument list
      res[i] = Zero_Tensor<U, M...>();
               ^
src/mytensor.hpp(112): note: this candidate was rejected because function is not visible
  myTensor_t<U, N> Zero_Tensor(){
                   ^
src/mytensor.hpp(104): note: this candidate was rejected because at least one template argument could not be deduced
  myTensor_t<U, N, M...> Zero_Tensor(){
                         ^
          detected during:
            instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=5UL, M=<>]" at line 107
            instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=3UL, M=<5UL>]" at line 107
            instantiation of "myTensor_t<U, N, M...> Zero_Tensor<U,N,M...>() [with U=int, N=3UL, M=<3UL, 5UL>]" at line 36 of "src/myclass.hpp"

我真的不明白this candidate was rejected because function is not visible 告诉我什么。我想我不明白为什么它不可见?任何帮助表示赞赏。

【问题讨论】:

  • res[i] = Zero_Tensor(); zero_tensor 中的这一行不会创建无限循环。
  • 请注意,Zero_Tensor 函数的定义可以简化为一行:return myTensor_t&lt;U, N, M...&gt;{};。值初始化 std::array 值初始化数组的每个元素,对于像 int 这样的数字类型,会将它们归零。
  • @SudipGhimire 我认为递归已被myTensor_t&lt;U, N&gt; Zero_Tensor() 停止。至少这是我的理解,也许我的想法完全不正确。
  • @MilesBudnek 我明白你的意思,非常正确。但是,当我没有原始类型时会发生什么?我将把它用于最终具有明确定义的U(0) 的某些类类型。再往下走,而不是只进行零初始化,我将使用一些定义良好的U(something) 进行持续初始化。我从来没有在问题中解释过,所以我很抱歉。

标签: c++ variadic-templates explicit-instantiation


【解决方案1】:

建议:重写你的Zero_Tensor()函数如下

template <typename U>
U Zero_Tensor () 
 { return U(0); }

template <typename U, std::size_t N, std::size_t... M>
myTensor_t<U, N, M...> Zero_Tensor ()
 {
   myTensor_t<U, N, M...> res;

   for ( auto i = 0u ; i < N ; ++i )
      res[i] = Zero_Tensor<U, M...>();

   return res;
 }

现在你的问题是地面版本(递归结束,&lt;U, N&gt; 版本)是定义在之后递归版本(&lt;U, N, M...&gt;),所以当递归版本调用

 Zero_Tensor<U, M...>();

M... 包为空,编译器不知道仅接受 U 类型作为模板参数的 Zero_Tensor() 函数。

你可以颠倒定义的顺序(或者在递归情况之前声明基本情况)但是你还有另一个问题:当你调用时

 Zero_Tensor<U, M...>();

M... 包仅包含一个数字,您的调用不明确,因为两个版本都匹配。

解决方案:使用更简单的基础案例

template <typename U>
U Zero_Tensor () 
 { return U(0); }

并在递归情况之前之前定义它。

这样

 Zero_Tensor<U, M...>();

永远不会模棱两可,因为当 M... 包为空时,只有基本情况匹配,否则只有递归情况匹配。

离题建议:当您有一个从零到无符号数的循环时(如您的示例所示)

// ............V  N is std::size_t, an unsigned type
for(int i=0; i<N; i++)

使用无符号变量作为索引变量 (i) 以避免出现恼人的警告,例如“警告:不同符号的整数表达式的比较:‘int’和‘long unsigned int’”

【讨论】:

  • 太好了,感谢您引导我了解为什么它不起作用的逻辑。从这个解释中想到了一个问题,为什么它与模板结构一起工作?递归案例之后的基本案例工作正常。我错过了什么?如果您认为我的问题是微不足道的,请您指导我到阅读资源,以便我弄清楚吗?您的最终评论/建议是的,我同意正确处理我们的类型是一种好习惯,当某些事情意外无法追踪问题时,它可以使其更加透明。
  • @max66 有没有办法可以将所有内容都包装到一个类中
  • @Pana - 不幸的是,我不是语言层,但是...关于模板化结构...它之所以有效,是因为您有一个具有专业化的结构,而不是两个不同的(不直接相关的)功能。不同,编译器管理它的方式也不同。
  • @SudipGhimire - 抱歉,我不明白你想包装什么。您想要一个 myTensor(没有专门化),还是想要将 Zero_Tensor() 函数集成到 myTensor 结构(和专门化)中?
  • @max66 好的,我明白了该描述中的要点。再次感谢您,我认为这一切都由我决定。
【解决方案2】:

问题在于,当您在 M... 为空的情况下实例化 Zero_Tensor 时,Zero_Tensor 的另一个重载(只有 2 个模板参数)不可见。如果在参数包版本之前移动该重载,则在传入 2 个模板参数时会遇到重载不明确的问题。

您可以通过仅使用一个 Zero_Tensor 函数来解决此问题,该函数根据 M... 的大小决定要做什么,如下所示:

template<typename U, std::size_t N, std::size_t... M>                                                                                                                                                               
myTensor_t<U, N, M...> Zero_Tensor(){                                                                                                                                                                               
  myTensor_t<U, N, M...> res;                                                                                                                                                                                       
  for(int i=0; i<N; i++)                                                                                                                                                                                            
    if constexpr(sizeof...(M) > 0)
      res[i] = Zero_Tensor<U, M...>();                                                                                                                                                                                
    else 
      res[i] = U(0);     
  return res;                                                                                                                                                                                                       
};                                                                                                                                                                                                                  

这是demo。请注意,这需要 c++17。

【讨论】:

  • 也许我错了,但是看了你的代码,我觉得很遗憾没有constexpr版本的三元运算符。
  • @max66 我假设你的意思是这样可以工作res[i] = sizeof...(M) &gt; 0 ? Zero_Tensor&lt;U, M...&gt;() : U(0);?我明白你的意思:constexpr if 版本,其中类型可能不同。那会很好。
猜你喜欢
  • 2014-10-31
  • 1970-01-01
  • 1970-01-01
  • 2013-10-28
  • 2014-09-13
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多