通过自由函数或成员函数扩展的机制

包含标准的大量C ++库允许您调整对象以便在库中使用。选择通常在成员函数或同一名称空间中的自由函数之间。 我想知道函数和构造库代码用于调用一个调用这个“扩展”函数之一的调用,我知道这个决定必须在编译期间进行,并涉及模板。以下运行时伪代码不可能/无意义,原因超出了本问题的范围。
if Class A has member function with signature FunctionSignature
    choose &A.functionSignature(...)
else if NamespaceOfClassA has free function freeFunctionSignature
    choose freeFunctionSignature(...)
else
    throw "no valid extension function was provided"
上面的代码看起来像运行时代码:/。那么,库如何找出一个类所在的命名空间,它如何检测这三个条件,还有哪些需要避免的陷阱。 我的问题的动机是让我能够在库中找到调度块,并能够在我自己的代码中使用这些构造。所以,详细的答案将有所帮助。 !!赢得胜利! 好的,根据Steve(和评论)的答案,ADL和SFINAE是在编译时连接调度的关键结构。我的头部是ADL(原始)和SFINAE(再次是粗鲁的)。但我不知道他们是如何以我认为应该的方式共同组织的。 我想看一个如何将这两个结构组合在一起的说明性示例,以便库可以在编译时选择是在对象中调用用户提供的成员函数,还是在同一对象的命名空间中提供的用户提供的自由函数。这应该只使用上面的两个构造来完成,没有任何类型的运行时分派。 让我们说有问题的对象叫做
NS::Car
,这个对象需要提供
MoveForward(int units)
的行为,作为c的成员函数。如果要从对象的命名空间中拾取行为,它可能看起来像
MoveForward(const Car & car_, int units)
。让我们定义想要发送的函数
mover(NS::direction d, const NS::vehicle & v_)
,其中direction是枚举,v_是
NS::car
的基类。     
已邀请:
好吧,我可以告诉你如何在编译时检测某个名称(和签名)的成员函数的存在。我的一个朋友在这里描述它: 在编译时检测成员函数的存在性 但是,这不会让你到达目的地,因为它只适用于静态类型。由于您希望传递“引用到车辆”,因此无法测试动态类型(引用后面的具体对象的类型)是否具有此类成员函数。 如果你满足于静态类型,还有另一种方法可以做一个非常相似的事情。 它实现“如果用户提供重载的自由函数,则调用它,否则尝试调用成员函数”。它是这样的:
namespace your_ns {

template <class T>
void your_function(T const& t)
{
    the_operation(t); // unqualified call to free function
}

// in the same namespace, you provide the "default"
// for the_operation as a template, and have it call the member function:

template <class T>
void the_operation(T const& t)
{
    t.the_operation();
}

} // namespace your_ns
这样用户可以提供自己的“the_operation”重载, 在与他的类相同的命名空间中,所以它是由ADL发现的。当然 用户的“the_operation”必须比默认“更专业” 实现 - 否则调用将是模糊的。 在实践中,这不是一个问题,因为一切限制 参数的类型多于它对任何东西的引用 “更专业”。 例:
namespace users_ns {

class foo {};

void the_operation(foo const& f)
{
    std::cout << "foon";
}

template <class T>
class bar {};

template <class T>
void the_operation(bar<T> const& b)
{
    std::cout << "barn";
}

} // namespace users_ns
编辑:在再次阅读Steve Jessop的回答之后,我意识到这基本上就是他写的,只有更多的话:)     
库在运行时不执行任何操作,在编译调用代码时由编译器完成调度。根据称为“参数依赖查找”(ADL)的机制的规则,找到与其中一个参数相同的名称空间中的自由函数,有时称为“Koenig查找”。 如果您可以选择实现自由函数或成员函数,则可能是因为库为调用成员函数的自由函数提供了模板。然后,如果您的对象通过ADL提供相同名称的函数,那么它将比实例化模板更好地匹配,因此将首先选择。正如Space_C0wb0y所说,他们可能会使用SFINAE来检测模板中的成员函数,并根据它是否存在来执行不同的操作。 你不能通过向
x
添加成员函数来改变
std::cout << x;
的行为,所以我不太清楚你的意思。     
如果您只是在寻找具体示例,请考虑以下事项:
#include <cassert>
#include <type_traits>
#include <iostream>

namespace NS
{
    enum direction { forward, backward, left, right };

    struct vehicle { virtual ~vehicle() { } };

    struct Car : vehicle
    {
        void MoveForward(int units) // (1)
        {
            std::cout << "in NS::Car::MoveForward(int)n";
        }
    };

    void MoveForward(Car& car_, int units)
    {
        std::cout << "in NS::MoveForward(Car&, int)n";
    }
}

template<typename V>
class HasMoveForwardMember // (2)
{
    template<typename U, void(U::*)(int) = &U::MoveForward>
    struct sfinae_impl { };

    typedef char true_t;
    struct false_t { true_t f[2]; };

    static V* make();

    template<typename U>
    static true_t check(U*, sfinae_impl<U>* = 0);
    static false_t check(...);

public:
    static bool const value = sizeof(check(make())) == sizeof(true_t);
};

template<typename V, bool HasMember = HasMoveForwardMember<V>::value>
struct MoveForwardDispatcher // (3)
{
    static void MoveForward(V& v_, int units) { v_.MoveForward(units); }
};

template<typename V>
struct MoveForwardDispatcher<V, false> // (3)
{
    static void MoveForward(V& v_, int units) { NS::MoveForward(v_, units); }
};

template<typename V>
typename std::enable_if<std::is_base_of<NS::vehicle, V>::value>::type // (4)
mover(NS::direction d, V& v_)
{
    switch (d)
    {
    case NS::forward:
        MoveForwardDispatcher<V>::MoveForward(v_, 1); // (5)
        break;
    case NS::backward:
        // ...
        break;
    case NS::left:
        // ...
        break;
    case NS::right:
        // ...
        break;
    default:
        assert(false);
    }
}

struct NonVehicleWithMoveForward { void MoveForward(int) { } }; // (6)

int main()
{
    NS::Car v; // (7)
    //NonVehicleWithMoveForward v;  // (8)
    mover(NS::forward, v);
}
HasMoveForwardMember
(2)是一个元函数,用于在给定的类
V
中检查具有签名
void(V::*)(int)
的该名称的成员函数的存在。
MoveForwardDispatcher
(3)使用此信息调用成员函数(如果存在)或者回退到调用自由函数(如果不存在)。
mover
只需将
MoveForward
的调用委托给
MoveForwardDispatcher
(5)。 as-posted代码将调用
Car::MoveForward
(1),但如果删除,重命名该成员函数或更改其签名,则将调用
NS::MoveForward
。 另请注意,因为
mover
是模板,所以必须进行SFINAE检查以保留仅允许从
NS::vehicle
派生的对象传入
v_
(4)的语义。为了证明,如果一个注释(7)和注释(8),将使用类型为
NonVehicleWithMoveForward
(6)的对象调用
mover
,尽管事实是
HasMoveForwardMember<NonVehicleWithMoveForward>::value == true
,我们仍然想要禁止它。 (注意:如果您的标准库没有
std::enable_if
std::is_base_of
,请使用
std::tr1::
boost::
变体。) 通常使用这种代码的方式是始终调用自由函数,并根据类似于
MoveForwardDispatcher
的方式实现自由函数,这样自由函数只调用传入对象的成员函数(如果存在),而不必编写重载对于可能具有适当成员函数的每种可能类型的自由函数。     

bab

有时候,开发人员可以使用自由函数或类函数,可以互换,有些情况下,互相使用。 (1)当对象/类函数(“方法”)的大多数只影响对象,或者对象打算组成其他对象时,它们是首选的。
// object method
MyListObject.add(MyItemObject);
MyListObject.add(MyItemObject);
MyListObject.add(MyItemObject);
(2)当涉及多个对象时,自由(“全局”或“模块”)函数是优选的,并且对象不是彼此的部分/组合。或者,当函数使用普通数据时(没有方法的结构,基本类型)。
MyStringNamespace.MyStringClass A = new MyStringNamespace.MyStringClass("Mercury");
MyStringNamespace.MyStringClass B = new MyStringNamespace.MyStringClass("Jupiter"); 
// free function
bool X = MyStringNamespace.AreEqual(A, B);
当某些常用模块函数访问对象时,在C ++中,您有“friend关键字”,允许它们访问对象方法,而不考虑范围。
class MyStringClass {
  private:
    // ...
  protected:
    // ...
  // not a method, but declared, to allow access
  friend:
    bool AreEqual(MyStringClass A, MyStringClass B);
}

bool AreEqual(MyStringClass A, MyStringClass B) { ... }
在“几乎纯粹的面向对象”编程语言(如Java或C#)中,您不能使用自由函数,自由函数将被静态方法替换,这使得事情变得更加复杂。     
如果我理解正确,您的问题只需使用(可能是多个)继承来解决。你有一个名称空间自由功能:
namespace NS {
void DoSomething()
{
    std::cout << "NS::DoSomething()" << std::endl;
}
} // namespace NS
使用转发相同功能的基类:
struct SomethingBase
{
    void DoSomething()
    {
        return NS::DoSomething();
    }
};
如果从SomethingBase派生的某个类没有实现DoSomething()调用它将调用SomethingBase :: DoSomething() - > NS :: DoSomething():
struct A : public SomethingBase // probably other bases
{
    void DoSomethingElse()
    {
        std::cout << "A::DoSomethingElse()" << std::endl;
    }
};
如果派生自SomethingBase的另一个类B实现DoSomething()调用它将调用B :: DoSomething():
struct B : public SomethingBase // probably other bases

{
    void DoSomething()
    {
        std::cout << "B::DoSomething()" << std::endl;
    }
};
因此,在派生自SomethingBase的对象上调用DoSomething()将执行成员(如果存在),否则执行free函数。请注意,没有任何内容可以抛出,如果与您的调用不匹配,则会出现编译错误。
int main()
{
    A a;
    B b;
    a.DoSomething(); // "NS::DoSomething()"
    b.DoSomething(); // "B::DoSomething()"
    a.DoSomethingElse(); // "A::DoSomethingElse()"
    b.DoSomethingElse(); // error 'DoSomethingElse' : is not a member of 'B'
}
    

要回复问题请先登录注册