如何调用成员函数(如果存在),否则是自由函数



我有各种各样的类:

struct foo final { std::string toString() const { return "foo"; } };
struct bar final { };
std::string toString(const bar&) { return "<bar>"; }
struct baz final { std::string toString() const { return "baz"; } };
std::string toString(const baz& b) { return "<" + b.toString() + ">"; }
struct blarf final {};

有些成员具有函数toString()(foo(,有些仅具有toString()自由函数(bar(,有些同时具有(baz(,而有些两者都没有(blarf(。

我如何按偏好顺序调用这些函数:1(成员函数(如果存在(,2(自由函数(如果它存在(,以及3(否则(没有成员或自由toString()(实用程序(toString_()(。

我试过这个:

// https://stackoverflow.com/questions/1005476/how-to-detect-whether-there-is-a-specific-member-variable-in-class/1007175#1007175
namespace details
{
template<typename T>
inline std::string toString_(const T& t)
{
const void* pV = &t;
return "#" + std::to_string(reinterpret_cast<size_t>(pV));
}
// https://stackoverflow.com/a/9154394/8877
template<typename T>
inline auto toString_imp(const T& obj, int) -> decltype(obj.toString(), std::string())
{
return obj.toString();
}
template<typename T>
inline auto toString_imp(const T& obj, long) -> decltype(toString(obj), std::string())
{
return toString(obj);
}
template<typename T>
inline auto toString_imp(const T& obj, long long) -> decltype(toString_(obj), std::string())
{
return toString_(obj);
}
template<typename T>
inline auto toString(const T& obj) -> decltype(toString_imp(obj, 0), std::string())
{
return toString_imp(obj, 0);
}
}
namespace str
{
template<typename T>
std::string toString(const T& t)
{
return details::toString(t);
}
}

但这会产生一个编译器错误:

int main()
{
const foo foo;
std::cout << str::toString(foo) << "n"; // "foo"
const bar bar;
std::cout << str::toString(bar) << "n"; // "<bar>"
const baz baz;
std::cout << str::toString(baz) << "n"; // "baz"
const blarf blarf;
std::cout << str::toString(blarf) << "n"; // "#31415926539"
}

我使用的是C++14(不需要使用C++11(。

1> error C2672: 'details::toString': no matching overloaded function found
1> message : could be 'unknown-type details::toString(const T &)'
1> message : Failed to specialize function template 'unknown-type details::toString(const T &)'
1> message : see declaration of 'details::toString'
1> message : With the following template arguments:
1> message : 'T=T'
1> message : see reference to function template instantiation 'std::string str::toString<bar>(const T &)' being compiled
1>        with
1>        [
1>            T=bar
1>        ]

所以detail::toString_imp:有三个过载

auto toString_imp(const T& obj, int) -> decltype(obj.toString(), std::string())
auto toString_imp(const T& obj, long) -> decltype(toString(obj), std::string())
auto toString_imp(const T& obj, long long) -> decltype(toString_(obj), std::string())

你用toString_imp(obj, 0)打电话

问题是,从0(一个int(到longlong long的转换需要相同数量的转换(单个转换(,因此第二个和第三个过载都不会超过另一个。

你可以用更糟糕的转换来解决这个问题,比如椭圆转换:

auto toString_imp(const T& obj, int) -> decltype(obj.toString(), std::string())
auto toString_imp(const T& obj, long) -> decltype(toString(obj), std::string())
auto toString_imp(const T& obj, ...) -> decltype(toString_(obj), std::string())

或者制作另一个助手:

auto toString_imp2(const T& obj, int) -> decltype(toString(obj), std::string())
auto toString_imp2(const T& obj, long) -> decltype(toString_(obj), std::string())
auto toString_imp(const T& obj, int) -> decltype(obj.toString(), std::string())
auto toString_imp(const T& obj, long) { return toString_imp2(obj, 0); }

或者,如果你发现自己有3个以上的病例,你可以使用这个类:

template<int N> struct priority : priority<N-1> {};
template<> struct priority<0> {};
auto toString_imp(const T& obj, priority<2>) -> decltype(obj.toString(), std::string())
auto toString_imp(const T& obj, priority<1>) -> decltype(toString(obj), std::string())
auto toString_imp(const T& obj, priority<0>) -> decltype(toString_(obj), std::string())
// call: toString_imp(obj, priority<2>{})

(当你有更多的过载时,增加priority中的数字。这是有效的,因为优先级越低,你需要的基类转换就越多(

最新更新