这是 gcc 过载分辨率中的错误吗?

Is this an error in gcc's overload resolution?

本文关键字:错误 分辨率 gcc 这是      更新时间:2023-10-16

我正在编写一个资源管理类,名为Wraiiper。最终目标是能够编写类似的东西

Wraiiper wr(new ClassA(), destroyA); // cleanup with destroyA 
wr.push_back(new ClassB());          // cleanup with delete

所以它基本上是动态大小的,接受任何类型,并使用delete或自定义解除定位器进行删除。我想它所做的工作与std::vector<boost:any>在插入到各种元素的唯一指针时所做的相同。代码(coliru链接)遵循

#include <iostream>
#include <algorithm>
#include <vector>

namespace dpol
{ // destruction policies
    template<typename Func>
    struct Destruction
    {
        Func finalize; 
        Destruction(Func f) : finalize(f)
        {
        }
        template<typename T>
        void apply(T* data)
        {
            if (data) finalize(data); 
        }
    };
    template<>
    struct Destruction<void>
    {
        template<typename T>
        void apply(T* data)
        {
            delete data; 
        }
    };
} // destruction policies
class Wraiiper
{
    struct ResourceConcept
    {
        virtual ~ResourceConcept() {}
        virtual void* get() = 0; 
    };    
    template<typename T, typename Func>
    struct ResourceModel : ResourceConcept, dpol::Destruction<Func>
    {
        T* data; 
        ResourceModel(T* data)
            : dpol::Destruction<Func>()
            , data(data)
        { // a. One arg constructor
        }
        ResourceModel(T* data, Func f)
            : dpol::Destruction<Func>(f)
            , data(data)
        { // b. Two args constructor
        }
        ~ResourceModel()
        {
            dpol::Destruction<Func>::apply(data); 
        }
        void* get()
        {
            return data; 
        }
    };
    std::vector<ResourceConcept*> resource; 
public:
  template<typename T, typename Func>
  Wraiiper(T* data, Func f)
  {
      resource.push_back(new ResourceModel<T, Func>(data, f)); 
  }
  template<typename T>
  Wraiiper(T* data)
  {
      resource.push_back(new ResourceModel<T, void>(data)); 
  }
  ~Wraiiper()
  {
      while (!resource.empty())
      {
          delete resource.back(); 
          resource.pop_back(); 
      }
  }
  template<typename T, typename Func>
  T* push_back(T* data, Func f)
  {
      resource.push_back(new ResourceModel<T, Func>(data, f)); 
      return get<T*>(resource.size()-1); 
  }
  template<typename T>
  T* push_back(T* data)
  {
      resource.push_back(new ResourceModel<T, void>(data)); 
      return get<T*>(resource.size()-1); 
  }
  template<typename T>
  T  get(std::size_t i = 0)
  {
      return (T)resource.at(0)->get(); 
  }
};
struct A
{
    int val; 
    A(int x) : val(x) {}
};
void dela(A *arg) { delete arg; }
int main()
{
    Wraiiper wr(new A(2), dela); // compiles fine
    Wraiiper wr2(new double);    // error !
    return 0;
}

问题

以下错误困扰着我

main.cpp:在"structWraiiper::ResourceModel"的实例化中:

main.cpp:79:7:从'Wraiiper::Wraiiper(T*)[withT=double]'中需要

main.cpp:121:28:从这里开始需要

main.cpp:51:9:错误:无效的参数类型"void"

    ResourceModel(T* data, Func f)
    ^

main.cpp:51:9:错误:在声明"Wraiiper::ResourceModel::ResourceModel(T*,Func)"中

我的论点是,在实例化Wraiiper::ResourceModel<double, void>时应该选择带有一个自变量ResourceModel(T* data)的构造函数(a),因为我显式地用一个自变量调用它

这里发生了什么,为什么会出现错误,我该如何克服它?

FWIW此操作在Visual Studio 2012 中也失败(相同错误)

我的论点是,在实例化Wraiiper::ResourceModel时,应该选择带有一个参数ResourceModel(T*data)的构造函数(a),因为我用一个参数显式调用它

调用函数不是问题所在;实例化ResourceModel<double, void>是。您可能没有调用那个有问题的函数,但它在实例化时仍然必须在语义上有效:

[C++11: 14.7.1/1]:[..]类模板专门化的隐式实例化导致声明的隐式实例,而不是类成员函数、成员类、作用域成员枚举、静态数据成员和成员模板的定义或默认参数的隐式实化[..]

…并且只要它尝试采用double*void,则不满足该标准。

Clang的诊断输出使这一点稍微清晰:

clang++ -std=c++11 -O2 -pedantic -pthread main.cpp && ./a.out
main.cpp:51:37: error: argument may not have 'void' type
        ResourceModel(T* data, Func f)
                                    ^
main.cpp:79:30: note: in instantiation of template class 'Wraiiper::ResourceModel<double, void>' requested here
      resource.push_back(new ResourceModel<T, void>(data)); 
                             ^
main.cpp:121:14: note: in instantiation of function template specialization 'Wraiiper::Wraiiper<double>' requested here
    Wraiiper wr2(new double);    // error !
             ^
1 error generated.

您可以使用std::enable_if禁用它。否则,我想你需要专门研究ResourceModel<..., void>病例。另一种选择是将一个奇异函数发送到ResourceModel中,而不是根本不发送,尽管您仍然需要为其选择一个可通过的类型