如何获得模板函数以查看以后定义的其他全局方法
How can I get my templated function to see other global methods defined later?
(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;
}
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 这个失败的测试是将零添加到空指针未定义的行为、编译器错误还是其他什么?
- 如何创建没有特定定义的随机分布?uniform_int_distribution是从其他类继承的吗?
- error dllimport 函数的定义不允许在一个特定的联合中,而其他类、结构和联合将按预期导出
- 在命名空间中声明变量,在 main 中定义它,使其对所有其他文件可见
- 如何定义此"if block"中其他无效输入的值,以便在c ++中将字符串转换为对象?
- C++ 在多个其他类中使用单个类 - 编译时出现多个定义错误
- 错误 LNK1104:错误消息中显示的路径未在其他依赖项中定义
- GlM(OpenGl Mathematics)库阻止M_PI和其他定义的常量工作
- 如何使 extern 方法在共享库中定义,但使用 cmake 在其他目标中声明?
- 如何使用使用代替定义或其他C++功能
- 使用类的其他成员变量定义类的成员变量数组
- C++ 在构造函数中传递数组而不在其他地方定义它们
- 如何在不定义其他 fonction getValue() 的情况下获取私有值
- 在编译时获取枚举类大小,而无需定义其他元素
- 定义其他名称空间的少量用法可以吗
- c++如何定义其他开发人员需要调用的常量函数顺序
- 为什么我们能够在类定义中定义[static const int],而不能定义其他静态const类型
- bcc32 + nasm obj 链接错误(致命:"fir.asm":需要在 .def 文件中定义其他段)
- 定义其他类中的对象