如何修复模板内部重构decltype的错误

How to fix error refactoring decltype inside template

本文关键字:decltype 错误 重构 内部 何修复      更新时间:2023-10-16

edit可能无法完成,请参阅函数模板采用函数指针的干净实现,尽管答案1有一个C宏可以解决https://stackoverflow.com/a/18706623/2332068


我将一个函数传递到模板中,以成为构造函数的预提供参数,但也需要在该函数上使用decltype将函数类型传递给unique_ptr<...>模板实例化器(?这个词对吗)

如果我预先使用decltype作为额外的模板参数,但如果我在作为参数传递的函数的模板内部调用它,它就不会起作用。

我使用g++4.9.2,并在这里扩展了我的探索。调用unique_ptr子类的继承模板构造函数,其中我将unique_ptr子类化为具有固定的析构函数,我发现一些析构函数不返回void,所以我想要一个更通用的模板,不需要指定析构函数类型。

我当前的代码是:

void free_int(int* p) {
  delete p;
}
template<typename T, void (*D)(T*)>
class unique_dptr : public std::unique_ptr<T, decltype(D)> {
    public: unique_dptr(T* t) : std::unique_ptr<T, decltype(D)>(t, D) { };
};
using int_ptr = unique_dptr<int, ::free_int>;
int_ptr i(new int(2));

但请注意,void (*D)(T*)调用签名将析构函数限制为一个void函数,该函数使用指向T 的指针

给定以下形式的unique_ptr的正常使用:

unique_ptr<foo, decltype(&::free_foo)>

我想要这样的东西:

template<typename T, typename D>
class unique_gptr : public std::unique_ptr<T, decltype(&D)> {
    public: unique_gptr(T* t) : std::unique_ptr<T, decltype(&D)>(t, D) { };
};
using int_gptr = unique_gptr<int, ::free_int>;
int_gptr ig(new int(2));

但编译器讨厌它:

error: template argument 2 is invalid
class unique_gptr : public std::unique_ptr<T, decltype(&D)> {
                                                          ^

毫无疑问,古老的C宏风格的标记粘贴是我错误的目标。

我已经尝试从decltype(&D)中删除&,但这留下了错误:

error: argument to decltype must be an expression

但是这是可以的:

template<typename T, typename D, D F>
class unique_gptr : public std::unique_ptr<T, D> {
    public: unique_gptr(T* t) : std::unique_ptr<T, D>(t, F) { };
};
using int_gptr = unique_gptr<int, decltype(&::free_int), ::free_int>;
int_gptr ig(new int(2));

但我想知道我做错了什么,我无法将decltype(&::free_int)移动到模板中。


其他解决方案

我意识到我可以为其他固定的自由函数类型编写额外的专业知识,取代void(*)(void*)

我意识到我可以为我的类型覆盖std::default_delete

但这实际上是模板组合

中的一个练习

我认为对于c++11来说,实现您想要的是不可能的。

template<typename T, typename D>
class unique_gptr : public std::unique_ptr<T, decltype(&D)> {
    public: unique_gptr(T* t) : std::unique_ptr<T, decltype(&D)>(t, D) { };
};
using int_gptr = unique_gptr<int, ::free_int>;
int_gptr ig(new int(2));

请注意,decltype应用于D,后者被声明为typename。所以D是一种类型。但是decltype不能用于类型

首先,代码尝试获取一个类型(&)的地址。其次,decltype的自变量应该是表达式,而不是类型或"类型的地址"。为了更容易理解,我们可以说decltype希望它的自变量是一个"变量",就像下面的例子一样。

int main()
{
    int i = 10;
    decltype(i) j;
    decltype(int) k; /* Compiler error. */
    decltype(&int) l; /* Compiler error. */
    return 0;
}

您还希望编译器将D替换为::free_int。并且::free_int作为非类型模板参数传入。然而,D是一个类型模板参数。非类型模板参数以实际类型(例如intstruct a*或类型模板名称)开头。而类型模板参数以typenameclass开头。非类型模板参数的一个更简单的例子

template<int INIT>
void func2(void)
{
    decltype(INIT) j = INIT;
}
int main()
{
    func2<10>();
    return 0;
}

当传入像::free_int这样的函数指针时,需要一个非类型模板参数,该参数前面必须有一个类型。并且您希望函数指针的类型是"可替换的"。因此,函数指针的类型必须是类型模板参数。这使它们成为两个模板参数。

这就是template<typename T, typename D, D F>中需要三个模板参数的原因,这是您获得的最佳结果。

不能将decltype与类型一起使用,因为目标是获取变量的类型。但在decltype(&D)中,D是一种类型。

我宁愿传递一个模板函数:

template<typename T, typename D, D F>
class unique_gptr : public std::unique_ptr<T, D> {
    public: unique_gptr(T* t) : std::unique_ptr<T, D>(t, F) { };
};
template <typename T, typename D>
unique_gptr<T, D F> make_unique_gptr(T pointer, D deleter)
{
    return unique_gptr<T, D, deleter>(pointer);
}
auto ptr = make_unique(new int(2), ::free_int);

您可以使用一些宏魔术:

template<class T, class D, D d>
struct UniqueDeleter : public std::unique_ptr<T, D> {
    UniqueDeleter(T* t) : std::unique_ptr<T, D>(t, d) { };
};
template<class R, class T>
T decl_unique_deleter_ptr(R (*d)(T*));
#define DECL_UNIQUE_DELETER1(f) UniqueDeleter<decltype(decl_unique_deleter_ptr(&f)), decltype(&f), &f>
#define DECL_UNIQUE_DELETER2(T, f) UniqueDeleter<T, decltype(&f), &f>
#define DECL_UNIQUE_DELETER_GET_MACRO(_1, _2, NAME, ...) NAME
#define DECL_UNIQUE_DELETER_EXPAND(x) x
#define decl_unique_deleter(...) DECL_UNIQUE_DELETER_EXPAND(DECL_UNIQUE_DELETER_GET_MACRO( 
    __VA_ARGS__, DECL_UNIQUE_DELETER2, DECL_UNIQUE_DELETER1, _)(__VA_ARGS__))

现在你可以这样使用它:

decl_unique_deleter(int, free_int) ig(new int(2));

或者使用using和一些更神奇的东西:

using int_gptr = decl_unique_deleter(free_int); // If accepted pointer type matches.
int_gptr ig(new int(2));

我还可以推荐一种替代解决方案:

auto i = new int(2);
BOOST_SCOPE_EXIT_ALL(&) { delete i; };