通过自由函数或成员函数进行扩展的机制 加载的C++库,标准包括在内,允许您在库中使用对象。通常在同一命名空间中的成员函数或自由函数之间进行选择

通过自由函数或成员函数进行扩展的机制 加载的C++库,标准包括在内,允许您在库中使用对象。通常在同一命名空间中的成员函数或自由函数之间进行选择,c++,oop,templates,compile-time,library-design,C++,Oop,Templates,Compile Time,Library Design,我想知道库代码用来分派调用这些“扩展”函数之一的调用的机制和构造,我知道这个决定必须在编译时进行,并且涉及模板。以下运行时psuedocode不可能/无意义,原因不在此问题范围内 if Class A has member function with signature FunctionSignature choose &A.functionSignature(...) else if NamespaceOfClassA has free function freeFunctio

我想知道库代码用来分派调用这些“扩展”函数之一的调用的机制和构造,我知道这个决定必须在编译时进行,并且涉及模板。以下运行时psuedocode不可能/无意义,原因不在此问题范围内

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单位)
的行为,作为ofc的成员函数。如果要从对象的名称空间中提取行为,它可能看起来像
MoveForward(const Car&Car,int units)
。让我们定义要分派的函数
mover(NS::direction d,const NS::vehicle&v_)
,其中direction是一个枚举,v_.是
NS::car

的基类。库在运行时不执行任何操作,分派由编译器在编译调用代码时完成。根据称为“参数相关查找”(ADL)机制(有时称为“Koenig查找”)的规则,可以找到与其中一个参数位于同一命名空间中的自由函数

在您可以选择实现自由函数或成员函数的情况下,可能是因为库为调用成员函数的自由函数提供了模板。然后,如果您的对象通过ADL提供了相同名称的函数,那么它将比实例化模板更好匹配,因此将首先选择它。正如Space_C0wb0y所说,他们可能会使用SFINAE来检测模板中的成员函数,并根据它是否存在来做一些不同的事情


您无法更改std::cout的行为。好吧,我可以告诉您如何在编译时检测特定名称(和签名)的成员函数的存在。我的一位朋友在这里描述:

但是,这并不能让您达到您想要的目的,因为它只适用于静态类型。由于您希望传递“对车辆的引用”,因此无法测试动态类型(引用后面的具体对象的类型)是否具有这样的成员函数

但是,如果您满足于静态类型,那么还有另一种方法可以做非常类似的事情。 它实现了“如果用户提供了一个重载的自由函数,则调用它,否则尝试调用成员函数”。事情是这样的:

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
为您的{
模板
使您的_函数无效(T const&T)
{
_操作(t);//对free函数的非限定调用
}
//在同一名称空间中,提供“default”
//将_操作作为模板,并使其调用成员函数:
模板
使_操作无效(T const&T)
{
t、 _操作();
}
}//为您的\n命名名称空间
这样用户就可以提供自己的重载“the_operation”, 与他的类位于同一名称空间中,因此它由ADL找到。当然 用户的“theu操作”必须比默认值“更专业” 实现-否则调用将是不明确的。 但实际上这并不是一个问题,因为所有限制 参数的类型超过了对const和anything的引用 “更专业化”

例如:

namespace users_ns {

class foo {};

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

template <class T>
class bar {};

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

} // namespace users_ns
命名空间用户\n{
类foo{};
取消_操作(foo const&f)
{

std::cout当然,有时,开发人员可以使用自由函数或类函数,在某些情况下可以互换使用,以相互使用

(1) 当对象/类函数(“方法”)的大多数用途仅影响对象时,或者对象被嵌入以组成其他对象时,首选对象/类函数(“方法”)

// object method
MyListObject.add(MyItemObject);
MyListObject.add(MyItemObject);
MyListObject.add(MyItemObject);
(2) 当涉及多个对象,且对象不是彼此的一部分/组成部分时,或者当函数使用普通数据(无方法的结构、基本类型)时,优先使用自由(“全局”或“模块”)函数

当一些常见的模块函数访问对象时,C++中,有“朋友关键字”,允许他们访问对象方法,而不考虑范围。

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语言,在这里你不能有自由函数,自由函数被静态方法所取代,这使得事物更加复杂。

如果你只是在寻找一个具体的例子,考虑下面的:

#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);
}
#包括
#包括
#包括
名称空间NS
{
枚举方向{向前、向后、向左、向右};
结构车辆{virtual~vehicle(){};
结构车:车辆
{
无效向前移动(整数单位)/(1)
{

如果我理解正确的话,你的问题就可以用我的方法解决了
#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);
}
namespace NS {
void DoSomething()
{
    std::cout << "NS::DoSomething()" << std::endl;
}
} // namespace NS
struct SomethingBase
{
    void DoSomething()
    {
        return NS::DoSomething();
    }
};
struct A : public SomethingBase // probably other bases
{
    void DoSomethingElse()
    {
        std::cout << "A::DoSomethingElse()" << std::endl;
    }
};
struct B : public SomethingBase // probably other bases

{
    void DoSomething()
    {
        std::cout << "B::DoSomething()" << std::endl;
    }
};
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'
}