如何在模板中修复错误重构decltype

时间:2015-10-30 12:02:39

标签: c++ function templates c++11 decltype

编辑可能无法完成,请参阅Clean implementation of function template taking function pointer虽然答案1有一个C宏解决方法https://stackoverflow.com/a/18706623/2332068

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

如果我预先使用decltype作为额外的模板参数,那么它是有效的,但如果我在作为参数传递的函数的模板内调用它,则不行。

我正在使用g ++ 4.9.2,并在此扩展我的探索Calling inherited template constructor of unique_ptr subclass,其中我将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-macro风格标记粘贴是我错误的目标。

我尝试从&删除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

但这实际上是模板构图中的练习

3 个答案:

答案 0 :(得分:2)

您不能将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);

答案 1 :(得分:2)

我认为对于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作为non-type template参数传入。但是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>中三个模板参数的原因,这是你得到的最佳结果。

答案 2 :(得分:1)

你可以使用一些宏魔术:

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; };