简体   繁体   English

模板和重载决议

[英]templates and overload resolution

I'm reading the book "C++ templates, complete guide", and on chapter 22 it introduces the concept of type erasure with an example of a std::function-linke class: 我正在阅读“C ++模板,完整指南”一书,第22章介绍了类型擦除的概念,以及std :: function-linke类的示例:

#include "functorwrapper.hpp"

// primary template (declaration)
template <typename Signature>
class Function;

// partial class template specialization
template <typename ReturnType, typename... Args>
class Function<ReturnType(Args...)>
{
public:
    // constructors
    Function() : mFunctorWrapper(nullptr) {}                 // default constructor
    Function(const Function &);                              // copy constructor
    Function(Function &&);                                   // move constructor
    template <typename Functor> Function(Functor &&);        // generalized constructor

    // destructor
    ~Function() { delete mFunctorWrapper; }

    // copy/move operations
    Function &operator=(Function const &);                          // copy assignmaent operator
    Function &operator=(Function &&);                               // move assignment operator 
    template <typename Functor> Function &operator=(Functor &&);    // generalized assignment operator

    // overloaded function call operator
    ReturnType operator()(Args...);
private:
    FunctorWrapperBase<ReturnType(Args...)> *mFunctorWrapper;
};

template <typename ReturnType, typename... Args>
Function<ReturnType(Args...)>::Function(const Function &other) : mFunctorWrapper(nullptr)
{
    if (other.mFunctorWrapper)
        mFunctorWrapper = other.mFunctorWrapper->Clone();
}

template <typename ReturnType, typename... Args>
Function<ReturnType(Args...)>::Function(Function &&other) :  mFunctorWrapper(other.mFunctorWrapper)
{
    other.mFunctorWrapper = nullptr;
}

template <typename ReturnType, typename... Args>
template <typename Functor>
Function<ReturnType(Args...)>::Function(Functor &&functor)
{
    // remove reference if l-value (template type argument deduced as Functor &)
    mFunctorWrapper = new FunctorWrapper<typename std::remove_reference<Functor>::type, ReturnType(Args...)>(std::forward<Functor>(functor));
}

template <typename ReturnType, typename... Args>
Function<ReturnType(Args...)> &Function<ReturnType(Args...)>::operator=(const Function &other)
{
    mFunctorWrapper = other.mFunctorWrapper->Clone();

    return *this;
}

template <typename ReturnType, typename... Args>
Function<ReturnType(Args...)> &Function<ReturnType(Args...)>::operator=(Function &&other)
{
    mFunctorWrapper = other.mFunctorWrapper;
    other.mFunctorWrapper = nullptr;

    return *this;
}

template <typename ReturnType, typename... Args>
template <typename Functor>
Function<ReturnType(Args...)> &Function<ReturnType(Args...)>::operator=(Functor &&functor)
{
    mFunctorWrapper = new FunctorWrapper<typename std::remove_reference<Functor>::type, ReturnType(Args...)>(std::forward<Functor>(functor));
}

template <typename ReturnType, typename... Args>
ReturnType Function<ReturnType(Args...)>::operator()(Args... args)
{
    mFunctorWrapper->Invoke(args...);
}

this class just manages the memory allocated for an object of type FunctorWrapper, which is a class template that represents different kinds of functors (or callables). 这个类只管理为FunctorWrapper类型的对象分配的内存,FunctorWrapper是一个表示不同类型的仿函数(或callables)的类模板。

If I construct an object of type Function from a function object, a lambda or a pointer to a function it all goes well (I can call the object and the relative function is called). 如果我从一个函数对象构造一个Function类型的对象,一个lambda或一个指向函数的指针它一切顺利(我可以调用该对象并调用相对函数)。

But if I try to copy construct (or move construct) a Function from another Function, the compiler binds the call only to the constructor that takes an arbitrary object (the generalized constructor with template parameter Functor and an universal reference as function parameter), causing a crash. 但是如果我尝试从另一个Function复制构造(或移动构造)一个函数,编译器只会将调用绑定到带有任意对象的构造函数(带有模板参数Functor的通用构造函数和作为函数参数的通用引用),崩溃。

I thought that if I call the constructor like: 我想如果我调用构造函数,如:

Function<void(double)> fp4(&FreeFunction);
fp4(1.2);

Function<void(double)> fp5 = fp4;  // copy construction

the copy constructor should be called, since it's more specialized. 应该调用复制构造函数,因为它更专业。 I followed the example on the book, but I must be doing something wrong. 我按照书上的例子,但我一定做错了。

I think it's a defect in the book. 我认为这是书中的缺陷。

the copy constructor should be called, since it's more specialized 应该调用复制构造函数,因为它更专业

template <typename Functor> Function(Functor &&); is a better match. 是一个更好的匹配。

After typename Functor is deduced as Function<...> & , the constructor turns into Function(Function &); 在将typename Functor推导为Function<...> & ,构造函数变为Function(Function &); , which is a better match than Function(const Function &); ,这是一个比Function(const Function &);更好的匹配Function(const Function &); if you pass a non-const object to it. 如果你传递一个非const对象。

You can fix this with SFINAE: 您可以使用SFINAE解决此问题:

template
<
    typename Functor,
    typename = std::enable_if_t<!std::is_same_v<Function,
        std::remove_cv_t<std::remove_reference_t<Functor>>>>
>
Function(Functor &&);

You need to do the same thing with the assignment operator. 您需要使用赋值运算符执行相同的操作。 Alternatively, you could simply remove it (assigning a functor should still work, as the compiler should call Function(Functor &&) followed by the move assignment). 或者,你可以简单地删除它(分配一个仿函数仍然可以工作,因为编译器应该调用Function(Functor &&)然后调用move赋值)。

Yes, unfortunately the forwarding reference version is a better match than the copy constructor, which requires an implicit conversion to the reference to const . 是的,遗憾的是转发引用版本比复制构造函数更好,这需要隐式转换为对const的引用。

You can put constraints as 你可以把约束作为

template <typename Functor, std::enable_if_t<!std::is_base_of_v<Function, std::decay_t<Functor>>>* = nullptr> 
Function(Functor &&);  

PS: I used std::is_base_of instead of std::is_same , for the case that Function might be inherited, and in the implementation of copy constructor of the derived class, the copy constructor of Function might be invoked with an argument with the derived class type. PS:我使用std::is_base_of而不是std::is_same ,对于可能继承Function的情况,并且在派生类的复制构造Function可以使用带派生的参数调用Function的复制构造Function班级类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM