我正在编写一些模板代码来确定给定类型是否可以作为任何参数传递给函数的任何可用重载。在下面的示例中,我使用了日志功能,但我也在数学库中的其他代码上尝试了此代码,结果是相同的。我们的想法是使用函数重载和sizeof运算符来区分有问题的类型可以合法地传递给有问题的函数的情况(在本例中为log)。
如果有效,我们会sizeof(overload<type>(NULL)) == sizeof(True)
当'type'可以合法地传递给log,而sizeof(overload<type>(NULL)) == sizeof(False)
则可以。std::string
。这似乎适用于大多数类型,但sizeof(overload<std::string>(NULL)) == sizeof(False)
失败。
以下是它失败的原因:
在正常情况下,我们应该log(std::string)
。但是,当我声明一个带有字符串的日志重载时,它仍然不会触发逻辑的sizeof(True)分支。请注意,我实际上并不想声明sizeof(True)
函数,我只是测试此代码以确保它能够检测到所有可能的重载。
起初我以为它没有正确检测到重载,但是当我用一个用户定义的类(下面的例子中的'MyClass')尝试它时,它工作正常:它在log(MyClass)
时产生#include <iostream>
#include <math.h>
template<int>
struct TakesInt{};
struct True
{
};
struct False
{
// guarantees that sizeof(False) != sizeof(True)
True array[2];
};
// takes anything; fall back if no match can be found
template<typename T>
False overload(...);
// takes a specific type; does not actually call log
template<typename T>
True overload(TakesInt<sizeof(log(T()))>*);
// As a test, this is an overload of log that takes a string.
// I don't actually want to implement this, but it should make the compiler
// think that a string is a valid argument.
double log(std::string);
// a placeholder for user defined class; could really be anything,
// like an arbitrary number class
struct MyClass{};
// declaring log for the arbitrary class above
// note that this is the same as for the log(std::string)
// if one works, the other should
double log(MyClass);
int main()
{
std::cout << sizeof(True) << '\t' << sizeof(False) << std::endl;
std::cout << sizeof(overload<std::string>(NULL)) << std::endl;
std::cout << sizeof(overload<double>(NULL)) << std::endl;
std::cout << sizeof(overload<MyClass >(NULL)) << std::endl;
return 0;
}
声明了{1}},否则为sizeof(False)。
{{1}}
答案 0 :(得分:2)
这与SFINAE分散注意力的问题相同:
#include <iostream>
namespace ns
{
struct string {};
}
void bar(...) { std::cout << "void bar(...)\n"; }
template<class T>
void foo()
{
T x{};
bar(x);
}
void bar(ns::string) { std::cout << "void bar(ns::string)\n"; }
int main()
{
foo<int>();
foo<ns::string>();
}
输出:
void bar(...) void bar(...)
将执行依赖函数名称的查找:
因此,以下示例有所不同:
#include <iostream>
namespace ns
{
struct string {};
}
void bar(...) { std::cout << "void bar(...)\n"; }
template<class T>
void foo()
{
T x{};
bar(x);
}
namespace ns
{
void bar(ns::string) { std::cout << "void bar(ns::string)\n"; }
}
int main()
{
foo<int>();
foo<ns::string>();
}
输出:
void bar(...) void bar(ns::string)
对于std::string
,唯一关联的命名空间是std
。全局命名空间不关联,不会在OP的代码中搜索。因此,在模板定义之后声明的重载将无法找到。
N.B。请不要在命名空间std
中注入重载。根据[namespace.std] / 1,这将导致未定义的行为。