如何获得模板函数以查看以后定义的其他全局方法

How can I get my templated function to see other global methods defined later?

本文关键字:定义 其他 方法 全局 何获得 函数      更新时间:2023-10-16

(Oktalist在下面给出了一个很好的答案,请检查一下和下面的评论,以帮助演示我们讨论过的所有内容,我在底部添加了完整的编译解决方案我的问题展示了所有讨论的内容。)

我有一个名称空间全局方法和模板方法的集合,如以下内容:

namespace PrettyPrint
{
    String to_string(bool val);
    String to_string(char val);
    String to_string(int val);
    String to_string(uint val);
    // ETC...
    template <typename T> String to_string(const T* val)
    {
        if (! val) return U("NULL");
        return String().copy_formatted("(%p)-> %S", (const void*)val, to_string(*val).uchars());
    }
    // ... and more templates to help with containers and such
}

"字符串"类型不是C 字符串,它是从IBM的ICU库中得出的特殊类,但与此问题无关。重点是,我有一堆名为to_string的名称空间全局方法,还有一些覆盖它们的模板函数。到目前为止,一切都很好,一切都很好。但是,现在我有了另一个标题,我有类似以下定义的标题:

namespace User 
{
    struct Service {
        int code;
        String name;
    }
    //...
}
namespace PrettyPrint
{
    String to_string(const User::Service& val) { return val.name; }
}

因此,现在我在其他地方定义了其他类型,并且我还定义了我的漂亮打印命名空间中的另一个to_string覆盖,以指示如何将我的新类型转换为字符串。将两个标题扔进一个文件中,类似的东西:

#include <the to_string and templates header>
#include <the User::Service header>
main() {
    User::Service s = {1, U("foo")};
    User::Service *p = &s;
    PrettyPrint::to_string(s);
    PrettyPrint::to_string(p);
}

(是的,to_string方法实际上应该在某个地方返回一个值,而不是重点。)关键是,第二个调用给出了编译器错误(GCC,btw),说在模板to_string方法中,没有匹配的函数为了致电" to_string(const user :: service&amp;)',这完全匹配定义和包含的我的方法。如果我扭转了#include订购的效果,则可以正常工作。

所以,我推测该模板仅查看其前面定义的方法。有什么解决方案吗?鉴于我的项目范围和复杂的#包括的范围,简单地说"始终确保它们以正确的顺序达到"不是一个可处理的解决方案,并且会在代码中引入过多的复杂脆弱性。基本TO_STRING定义是那些往往会在许多地方升起的文件之一,因此请确保任何其他恰好包含to_string Override的随机类型定义首先无法正常工作。<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<</p>

我在某些地方使用的另一个解决方案是我在基本文件中定义了一个可打印的接口:

namespace PrettyPrint
{
    class Printable {
    public:
        virtual String pretty_print_to_string() const = 0;
    }
    String to_string(const Printable& obj) { return obj.pretty_print_to_string(); }
}

该定义是在同一文件中的模板方法之前进行的。因此,这对于可以简单地添加该界面并实现的课程非常有用。除了这里的任何出色解决方案之外不依赖#include下令工作。

无论如何,你们都如何看待这些选择?我没有想到的方法可能会很好吗?

从答案启发的解决方案给出了

此代码实际上确实编译了,因此您可以将其复制并使用它,我认为我已经捕获了所有相关用例以及什么无效,无效以及原因。

#include <iostream>
using namespace std;
namespace PrettyPrint
{
    void sample(int val) { cout << "PrettyPrint::sample(int)n"; }
    void sample(bool val) { cout << "PrettyPrint::sample(bool)n"; }
    template<typename T> void sample(T* val) { cout << "PrettyPrint::sample(pointer); -> "; sample(*val); }
}
namespace User
{
    struct Foo {
        int i;
        bool b;
    };
    void sample(const Foo& val) {
        //below doesn't work un-qualified, tries to convert the int (val.i) into a Foo to make a recursive call.
        //meaning, it matches the User namespace version first
        //sample(val.i); doesn't work, tries to call User::sample(const Foo&)
        cout << "User::sample(const Foo&); -> {n";
        cout << 't'; PrettyPrint::sample(val.i); //now it works
        cout << 't'; PrettyPrint::sample(val.b);
        cout << "}n";
    }
}
namespace Other
{
    void test(User::Foo* fubar) {
        cout << "In Other::test(User::Foo*):n";
        //PrettyPrint::sample(*fubar); //doesn't work, can't find sample(const User::Foo&) in PrettyPrint
        PrettyPrint::sample(fubar); //works, by argument-dependent lookup (ADL) from the template call
        sample(*fubar); //works, finds the method by ADL
        //sample(fubar); //doesn't work, only sees User::sample() and can't instantiate a Foo& from a Foo*
    }
    void test2(User::Foo* happy) {
        using PrettyPrint::sample; //now both work! this is the way to do it.
        cout << "In Other::test2(User::Foo*):n";
        sample(*happy);
        sample(happy);
    }
}
int main() {
    int i=0, *p = &i;
    bool b=false;
    User::Foo f = {1, true}, *pf = &f;
    //sample(i);  <-- doesn't work, PrettyPrint namespace is not visible here, nor is User for that matter.
    PrettyPrint::sample(i); //now it works
    //PrettyPrint::sample(f); //doesn't work, forces search in PrettyPrint only, doesn't see User override.
    using namespace PrettyPrint;  // now they all work.
    sample(p);
    sample(b);
    sample(f);
    sample(pf);
    Other::test(pf);
    Other::test2(pf);
    return 0;
}

这将导致以下输出:

PrettyPrint::sample(int)
PrettyPrint::sample(pointer); -> PrettyPrint::sample(int)
PrettyPrint::sample(bool)
User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}
PrettyPrint::sample(pointer); -> User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}
In Other::test(User::Foo*):
PrettyPrint::sample(pointer); -> User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}
User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}
In Other::test2(User::Foo*):
User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}
PrettyPrint::sample(pointer); -> User::sample(const Foo&); -> {
    PrettyPrint::sample(int)
    PrettyPrint::sample(bool)
}

在两个阶段查找的第一阶段,当定义模板时,在即时的封闭名称空间中寻找依赖和非依赖性名称模板并仅查找模板定义之前出现的那些to_string过载。

在两个阶段查找的第二阶段中,当模板实例化时,依赖参数依赖性查找在与任何类型的类型相关联的名称中查找了相关名称,该名称作为参数传递给了命名函数。但是,由于您的to_string(const User::Service&)过载在PrettyPrint名称空间中,因此不会通过参数依赖性查找找到。

将您的to_string(const User::Service&)重载移至User名称空间中以使用与参数有关的查找,该查找将在模板实例化点上找到任何声明的过载,包括在模板定义点之后声明的任何声明。

另请参见http://clang.llvm.org/compatibility.html#dep_lookup

编译器实例化模板时的作用本质上是在扩展它(或多或少像宏),并即时汇总结果。为此,当时必须可见提到的任何功能(或其他内容)。因此,请确保事先提及使用的任何声明(在同一标头文件中,很可能)。

我不完全理解这里发生了什么,但是看来,如果您希望使用后来的定义方法,那么它们需要是模板专业化。编译器在程序以后不会看到功能覆盖。

首先,我们重现了问题。我在GCC-4.8.2中使用此程序这样做:

// INCLUDE FILE 1
template <class T> void to_string (T const * a) {
  to_string (*a);
}
// INCLUDE FILE 1 END
// INCLUDE FILE 2
// How do we make this program work when this is necessarily declared after to_string(T const * A)?
void to_string(int const & a) {
  return;
}
// INCLUDE FILE 2 END
int main (void) {
  int i = 5;
  int * p = &i;
  to_string(i);
  to_string(p);
  to_string(&i);
  return 0;
}

为了使其正常工作,我们需要做到这一点...

// INCLUDE FILE 1
// The pointer version needs something to call...
template <class T> void to_string (T const & a);

// This can't be T const * a.  Try it.  So weird...
template <class T> void to_string (T * a) {
  foo (*a);
}
// INCLUDE FILE 1 END
// INCLUDE FILE 2
// This has to specialize template<> void to_string.  If we just override, we get a linking error.
template<> void to_string <int> (int const & a) {
  return;
}
// INCLUDE FILE 2 END
int main (void) {
  int i = 5;
  int * p = &i;
  to_string(i);
  to_string(p);
  to_string(&i);
  return 0;
}
相关文章: