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

Has*_*yed 10 c++ oop templates library-design compile-time

包含标准的大量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"
Run Code Online (Sandbox Code Playgroud)

上面的代码看起来像运行时代码:/.那么,库如何找出一个类所在的命名空间,它如何检测这三个条件,还有哪些需要避免的陷阱.

我的问题的动机是让我能够在库中找到调度块,并能够在我自己的代码中使用这些构造.所以,详细的答案将有所帮助.

!!赢得胜利!

好的,根据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.

Ste*_*sop 8

库在运行时不执行任何操作,在编译调用代码时由编译器完成调度.根据称为"参数依赖查找"(ADL)的机制的规则,找到与其中一个参数相同的名称空间中的自由函数,有时称为"Koenig查找".

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

你不能通过std::cout << x;添加成员函数来改变行为x,所以我不太清楚你的意思.


Pau*_*oke 2

好吧,我可以告诉您如何在编译时检测特定名称(和签名)的成员函数的存在。我的一个朋友在这里描述了它:

在编译时检测成员函数是否存在

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

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

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
Run Code Online (Sandbox Code Playgroud)

这样,用户可以在与其类相同的命名空间中提供自己的“the_operation”重载,以便 ADL 找到它。当然,用户的“the_operation”必须比您的默认实现“更专业” - 否则调用将不明确。但实际上这不是问题,因为对参数类型的限制超过对任何内容的 const 引用的所有内容都将“更专业”。

例子:

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
Run Code Online (Sandbox Code Playgroud)

编辑:再次阅读史蒂夫·杰索普的回答后,我意识到这基本上就是他写的,只是多了一些字:)