我有一个非常简单的问题:某个地方有一个功能
int size (const C & c)
至少可以通过参数依赖的名称查找找到。现在问题是:
struct B
{
int size () { /* ... */ }
void doSomething (const C & c)
{
int x = size (c); // <----------- problem!
// ...
}
}
这不起作用,因为名称查找在找到成员函数后停止。
我必须在指示的行中写一下,以便尝试调用 not 成员函数,但是,相反,如果成员函数没有,编译器会执行任何操作存在?
请注意,该解决方案不正在撰写::size
,因为这会阻止argument-dependent name lookup,并且仅在我知道size
声明的位置时才有效。
进一步的复杂化:
我知道对于我使用下面模板化成员函数T
的每个相关类型B::doSomething
,某处会有一个函数
int size (const T & t)
至少可以通过参数依赖的名称查找找到。 B
如下所示:
struct B
{
int size () { /* ... */ }
template<class T>
void doSomething (const T & t)
{
int x = size (t); // <----------- problem!
// ...
}
}
我希望调用非成员函数(我确信它存在,但我无法确定它在哪里)。
答案 0 :(得分:10)
这是一个众所周知的问题,其解决方案同样众所周知。我很惊讶它尚未被提及。如果您有非成员函数,例如:
class C;
size_t size( C const &c );
您可以使名称查找优先于具有using
声明的成员函数:
struct B {
size_t size();
void foo( C const &c ) {
using ::size;
size_t sz = size(c);
}
};
当编译器看到对size(c)
的调用时,它会从最里面的范围开始,向外搜索名为size
的内容。如果没有using
声明,编译器会在全局命名空间中的非成员之前找到类范围中的成员函数,但using
声明会更改此值。最里面的范围是函数本身,并且在成员函数之前找到using声明。
这样做的好处在于您仍然可以获得与参数相关的查找(ADL),因为对size(c)
的实际调用是不合格的。这意味着您可以在模板中使用它:
template <class T>
void foo( T const &c ) {
using ::size;
size_t sz = size(c);
}
...即使正确的size
函数在另一个命名空间中,它也可以由ADL找到。 using
声明只需要引用某些 size
函数,而不一定是您真正想要的函数。在一个可能调用成员的地方有一个默认实现是很正常的。下一版本的C ++标准(C ++ 17)几乎肯定会有一个std::size
函数来完成它。一旦广泛使用,您就可以编写
using std::size;
size_t sz = size(c);
目前,您可以提供自己的默认实现,例如:
template <class C>
constexpr auto size( C const &c ) -> decltype(c.size()) {
return c.size();
}
...或者您可以继续引用C
的版本,并依赖ADL找到合适的版本。
答案 1 :(得分:6)
如果你不能重命名你自己的成员函数,你可以使用一个肮脏的技巧:
static inline int dirty_trick(C const & c)
{
return size(c);
}
void B::doSomething(C const & c)
{
int x = dirty_trick(c);
// ...
}
答案 2 :(得分:3)
为了完整性,并加入理查德史密斯接受的答案:
我的解决方案现在看起来如下:
namespace adl {
// This declaration's only purpose is the possibility to refer to
// a non-member function named "size" in a using declaration.
//
// The signature does not matter, so we choose the easiest possible one.
void size ();
}
struct B
{
int size () { /* ... */ }
template<class T>
void doSomething (const T & t)
{
using adl::size;
int x = size (t); // <----------- no problem anymore
// ...
}
};
这样我就不必包含任何实际可能不需要的标题。
答案 3 :(得分:0)
您可以使用SFINAE来检测您是否具有成员功能。
我使用此answer为大小准备此类模板测试:
template <typename B>
class has_size
{
template <typename T, T> struct TypeCheck;
typedef char Yes;
typedef struct { char dummy[2]; } No;
template <typename T> struct Size
{
typedef int (T::*fptr)(const C&);
};
template <typename T> static Yes HasSize(TypeCheck< typename Size<T>::fptr, &T::size >*);
template <typename T> static No HasSize(...);
public:
static bool const value = (sizeof(HasSize<B>(0)) == sizeof(Yes));
};
现在,主要部分,2个功能 - 由于SFINAE只会选择其中一个:
#include <type_traits>
template <class B>
int callSize(B* obj, const C& c, typename std::enable_if<has_size<B>::value>::type* = 0)
{
obj->size(c);
}
template <class B>
int callSize(B*, const C& c, typename std::enable_if<!has_size<B>::value>::type* = 0)
// ^ node this !
{
return size(c);
}
现在,以自动魔术的方式 - 取决于你是否有B :: size(C) - 将调用全局或本地函数:
struct B
{
int size () { /* ... */ };
template <class U> friend int ::callSize(U*, const C&, typename std::enable_if<has_size<U>::value>::type*);
template <class U> friend class has_size;
void doSomething (const C & c)
{
int x = ::callSize(this, c); // <----------- problem!
// ...
}
private:
#ifdef LOCAL_SIZE
int size (const C & c) {
std::cout << "local size!\n";
return 0;
}
#endif
};
int main() {
B b;
b.doSomething(C());
}
输出Global size
。获取Local size
- 只需启用本地B::size(C)
功能。用其他答案来证明这一点,即
工作示例here at ideone